http 0.5.1 → 0.6.0.pre
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.
Potentially problematic release.
This version of http might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +3 -3
- data/.rspec +3 -2
- data/.rubocop.yml +101 -0
- data/.travis.yml +19 -8
- data/Gemfile +24 -6
- data/LICENSE.txt +1 -1
- data/README.md +144 -29
- data/Rakefile +23 -1
- data/examples/parallel_requests_with_celluloid.rb +2 -2
- data/http.gemspec +14 -14
- data/lib/http.rb +5 -4
- data/lib/http/authorization_header.rb +37 -0
- data/lib/http/authorization_header/basic_auth.rb +24 -0
- data/lib/http/authorization_header/bearer_token.rb +29 -0
- data/lib/http/backports.rb +2 -0
- data/lib/http/backports/base64.rb +6 -0
- data/lib/http/{uri_backport.rb → backports/uri.rb} +10 -10
- data/lib/http/chainable.rb +24 -25
- data/lib/http/client.rb +97 -67
- data/lib/http/content_type.rb +27 -0
- data/lib/http/errors.rb +13 -0
- data/lib/http/headers.rb +154 -0
- data/lib/http/headers/mixin.rb +11 -0
- data/lib/http/mime_type.rb +61 -36
- data/lib/http/mime_type/adapter.rb +24 -0
- data/lib/http/mime_type/json.rb +23 -0
- data/lib/http/options.rb +21 -48
- data/lib/http/redirector.rb +12 -7
- data/lib/http/request.rb +82 -33
- data/lib/http/request/writer.rb +79 -0
- data/lib/http/response.rb +39 -68
- data/lib/http/response/body.rb +62 -0
- data/lib/http/{response_parser.rb → response/parser.rb} +3 -1
- data/lib/http/version.rb +1 -1
- data/logo.png +0 -0
- data/spec/http/authorization_header/basic_auth_spec.rb +29 -0
- data/spec/http/authorization_header/bearer_token_spec.rb +36 -0
- data/spec/http/authorization_header_spec.rb +41 -0
- data/spec/http/backports/base64_spec.rb +13 -0
- data/spec/http/client_spec.rb +181 -0
- data/spec/http/content_type_spec.rb +47 -0
- data/spec/http/headers/mixin_spec.rb +36 -0
- data/spec/http/headers_spec.rb +417 -0
- data/spec/http/options/body_spec.rb +6 -7
- data/spec/http/options/form_spec.rb +4 -5
- data/spec/http/options/headers_spec.rb +9 -17
- data/spec/http/options/json_spec.rb +17 -0
- data/spec/http/options/merge_spec.rb +18 -19
- data/spec/http/options/new_spec.rb +5 -19
- data/spec/http/options/proxy_spec.rb +6 -6
- data/spec/http/options_spec.rb +3 -9
- data/spec/http/redirector_spec.rb +100 -0
- data/spec/http/request/writer_spec.rb +25 -0
- data/spec/http/request_spec.rb +54 -14
- data/spec/http/response/body_spec.rb +24 -0
- data/spec/http/response_spec.rb +61 -32
- data/spec/http_spec.rb +77 -86
- data/spec/spec_helper.rb +25 -2
- data/spec/support/example_server.rb +58 -49
- data/spec/support/proxy_server.rb +27 -11
- metadata +60 -55
- data/lib/http/header.rb +0 -11
- data/lib/http/mime_types/json.rb +0 -19
- data/lib/http/request_stream.rb +0 -77
- data/spec/http/options/callbacks_spec.rb +0 -62
- data/spec/http/options/response_spec.rb +0 -24
- data/spec/http/request_stream_spec.rb +0 -25
| @@ -18,7 +18,7 @@ module HTTP | |
| 18 18 | 
             
                  end
         | 
| 19 19 |  | 
| 20 20 | 
             
                  def http_version
         | 
| 21 | 
            -
                    @parser.http_version.join( | 
| 21 | 
            +
                    @parser.http_version.join('.')
         | 
| 22 22 | 
             
                  end
         | 
| 23 23 |  | 
| 24 24 | 
             
                  def status_code
         | 
| @@ -53,6 +53,8 @@ module HTTP | |
| 53 53 | 
             
                  end
         | 
| 54 54 |  | 
| 55 55 | 
             
                  def reset
         | 
| 56 | 
            +
                    @parser.reset!
         | 
| 57 | 
            +
             | 
| 56 58 | 
             
                    @finished = false
         | 
| 57 59 | 
             
                    @headers  = nil
         | 
| 58 60 | 
             
                    @chunk    = nil
         | 
    
        data/lib/http/version.rb
    CHANGED
    
    
    
        data/logo.png
    ADDED
    
    | Binary file | 
| @@ -0,0 +1,29 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::AuthorizationHeader::BasicAuth do
         | 
| 4 | 
            +
              describe '.new' do
         | 
| 5 | 
            +
                it 'fails when options is not a Hash' do
         | 
| 6 | 
            +
                  expect { described_class.new '[FOOBAR]' }.to raise_error
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it 'fails when :pass is not given' do
         | 
| 10 | 
            +
                  expect { described_class.new :user => '[USER]' }.to raise_error
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                it 'fails when :user is not given' do
         | 
| 14 | 
            +
                  expect { described_class.new :pass => '[PASS]' }.to raise_error
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              describe '#to_s' do
         | 
| 19 | 
            +
                let(:user)        { 'foo' }
         | 
| 20 | 
            +
                let(:pass)        { 'bar' * 100 }
         | 
| 21 | 
            +
                let(:user_n_pass) { user + ':' + pass }
         | 
| 22 | 
            +
                let(:builder)     { described_class.new :user => user, :pass => pass }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                subject { builder.to_s }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                it { should eq "Basic #{Base64.strict_encode64 user_n_pass}" }
         | 
| 27 | 
            +
                it { should match(/^Basic [^\s]+$/) }
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::AuthorizationHeader::BearerToken do
         | 
| 4 | 
            +
              describe '.new' do
         | 
| 5 | 
            +
                it 'fails when options is not a Hash' do
         | 
| 6 | 
            +
                  expect { described_class.new '[TOKEN]' }.to raise_error
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it 'fails when :token is not given' do
         | 
| 10 | 
            +
                  expect { described_class.new :encode => true }.to raise_error
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              describe '#to_s' do
         | 
| 15 | 
            +
                let(:token)   { 'foobar' * 100 }
         | 
| 16 | 
            +
                let(:builder) { described_class.new options.merge :token => token }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                subject { builder.to_s }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                context 'when :encode => true' do
         | 
| 21 | 
            +
                  let(:options) { {:encode => true} }
         | 
| 22 | 
            +
                  it { should eq "Bearer #{Base64.strict_encode64 token}" }
         | 
| 23 | 
            +
                  it { should match(/^Bearer [^\s]+$/) }
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                context 'when :encode => false' do
         | 
| 27 | 
            +
                  let(:options) { {:encode => false} }
         | 
| 28 | 
            +
                  it { should eq "Bearer #{token}" }
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                context 'when :encode not specified' do
         | 
| 32 | 
            +
                  let(:options) { {} }
         | 
| 33 | 
            +
                  it { should eq "Bearer #{token}" }
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::AuthorizationHeader do
         | 
| 4 | 
            +
              describe '.build' do
         | 
| 5 | 
            +
                context 'with unkown type' do
         | 
| 6 | 
            +
                  let(:type) { :foobar }
         | 
| 7 | 
            +
                  let(:opts) { {:foo => :bar} }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  it 'fails' do
         | 
| 10 | 
            +
                    expect { described_class.build type, opts }.to raise_error
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                context 'with :basic type' do
         | 
| 15 | 
            +
                  let(:type) { :basic }
         | 
| 16 | 
            +
                  let(:opts) { {:user => 'user', :pass => 'pass'} }
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  it 'passes options to BasicAuth' do
         | 
| 19 | 
            +
                    expect(described_class::BasicAuth).to receive(:new).with(opts)
         | 
| 20 | 
            +
                    described_class.build type, opts
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                context 'with :bearer type' do
         | 
| 25 | 
            +
                  let(:type) { :bearer }
         | 
| 26 | 
            +
                  let(:opts) { {:token => 'token', :encode => true} }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                  it 'passes options to BearerToken' do
         | 
| 29 | 
            +
                    expect(described_class::BearerToken).to receive(:new).with(opts)
         | 
| 30 | 
            +
                    described_class.build type, opts
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              describe '.register' do
         | 
| 36 | 
            +
                it 'registers given klass in builders registry' do
         | 
| 37 | 
            +
                  described_class.register :dummy, Class.new { def initialize(*); end }
         | 
| 38 | 
            +
                  expect { described_class.build(:dummy, 'foobar') }.to_not raise_error
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
            end
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe Base64 do
         | 
| 4 | 
            +
              specify { expect(Base64).to respond_to :strict_encode64 }
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              describe '.strict_encode64' do
         | 
| 7 | 
            +
                let(:long_string) { (0...256).map { ('a'..'z').to_a[rand(26)] }.join }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                it 'returns a String without whitespaces' do
         | 
| 10 | 
            +
                  expect(Base64.strict_encode64 long_string).to_not match(/\s/)
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| @@ -0,0 +1,181 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::Client do
         | 
| 4 | 
            +
              StubbedClient = Class.new(HTTP::Client) do
         | 
| 5 | 
            +
                def perform_without_following_redirects(request, options)
         | 
| 6 | 
            +
                  stubs.fetch(request.uri.to_s) { super(request, options) }
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def stubs
         | 
| 10 | 
            +
                  @stubs ||= {}
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def stub(stubs)
         | 
| 14 | 
            +
                  @stubs = stubs
         | 
| 15 | 
            +
                  self
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              def redirect_response(location, status = 302)
         | 
| 20 | 
            +
                HTTP::Response.new(status, '1.1', {'Location' => location}, '')
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              def simple_response(body, status = 200)
         | 
| 24 | 
            +
                HTTP::Response.new(status, '1.1', {}, body)
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              describe 'following redirects' do
         | 
| 28 | 
            +
                it 'returns response of new location' do
         | 
| 29 | 
            +
                  client = StubbedClient.new(:follow => true).stub(
         | 
| 30 | 
            +
                    'http://example.com/'     => redirect_response('http://example.com/blog'),
         | 
| 31 | 
            +
                    'http://example.com/blog' => simple_response('OK')
         | 
| 32 | 
            +
                  )
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  expect(client.get('http://example.com/').to_s).to eq 'OK'
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                it 'prepends previous request uri scheme and host if needed' do
         | 
| 38 | 
            +
                  client = StubbedClient.new(:follow => true).stub(
         | 
| 39 | 
            +
                    'http://example.com/'           => redirect_response('/index'),
         | 
| 40 | 
            +
                    'http://example.com/index'      => redirect_response('/index.html'),
         | 
| 41 | 
            +
                    'http://example.com/index.html' => simple_response('OK')
         | 
| 42 | 
            +
                  )
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  expect(client.get('http://example.com/').to_s).to eq 'OK'
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                it 'fails upon endless redirects' do
         | 
| 48 | 
            +
                  client = StubbedClient.new(:follow => true).stub(
         | 
| 49 | 
            +
                    'http://example.com/' => redirect_response('/')
         | 
| 50 | 
            +
                  )
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  expect { client.get('http://example.com/') } \
         | 
| 53 | 
            +
                    .to raise_error(HTTP::Redirector::EndlessRedirectError)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                it 'fails if max amount of hops reached' do
         | 
| 57 | 
            +
                  client = StubbedClient.new(:follow => 5).stub(
         | 
| 58 | 
            +
                    'http://example.com/'  => redirect_response('/1'),
         | 
| 59 | 
            +
                    'http://example.com/1' => redirect_response('/2'),
         | 
| 60 | 
            +
                    'http://example.com/2' => redirect_response('/3'),
         | 
| 61 | 
            +
                    'http://example.com/3' => redirect_response('/4'),
         | 
| 62 | 
            +
                    'http://example.com/4' => redirect_response('/5'),
         | 
| 63 | 
            +
                    'http://example.com/5' => redirect_response('/6'),
         | 
| 64 | 
            +
                    'http://example.com/6' => simple_response('OK')
         | 
| 65 | 
            +
                  )
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  expect { client.get('http://example.com/') } \
         | 
| 68 | 
            +
                    .to raise_error(HTTP::Redirector::TooManyRedirectsError)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              describe 'parsing params' do
         | 
| 73 | 
            +
                it 'accepts params within the provided URL' do
         | 
| 74 | 
            +
                  client = HTTP::Client.new
         | 
| 75 | 
            +
                  allow(client).to receive(:perform)
         | 
| 76 | 
            +
                  expect(HTTP::Request).to receive(:new) do |_, uri|
         | 
| 77 | 
            +
                    params = CGI.parse(URI(uri).query)
         | 
| 78 | 
            +
                    expect(params).to eq('foo' => ['bar'])
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  client.get('http://example.com/?foo=bar')
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                it 'combines GET params from the URI with the passed in params' do
         | 
| 85 | 
            +
                  client = HTTP::Client.new
         | 
| 86 | 
            +
                  allow(client).to receive(:perform)
         | 
| 87 | 
            +
                  expect(HTTP::Request).to receive(:new) do |_, uri|
         | 
| 88 | 
            +
                    params = CGI.parse(URI(uri).query)
         | 
| 89 | 
            +
                    expect(params).to eq('foo' => ['bar'], 'baz' => ['quux'])
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  client.get('http://example.com/?foo=bar', :params => {:baz => 'quux'})
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              describe 'passing json' do
         | 
| 97 | 
            +
                it 'encodes given object' do
         | 
| 98 | 
            +
                  client = HTTP::Client.new
         | 
| 99 | 
            +
                  allow(client).to receive(:perform)
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  expect(HTTP::Request).to receive(:new) do |*args|
         | 
| 102 | 
            +
                    expect(args.last).to eq('{"foo":"bar"}')
         | 
| 103 | 
            +
                  end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                  client.get('http://example.com/', :json => {:foo => :bar})
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
              describe '#request' do
         | 
| 110 | 
            +
                context 'with explicitly given `Host` header' do
         | 
| 111 | 
            +
                  let(:headers) { {'Host' => 'another.example.com'} }
         | 
| 112 | 
            +
                  let(:client)  { described_class.new :headers => headers }
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  it 'keeps `Host` header as is' do
         | 
| 115 | 
            +
                    expect(client).to receive(:perform) do |req, options|
         | 
| 116 | 
            +
                      expect(req['Host']).to eq 'another.example.com'
         | 
| 117 | 
            +
                    end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    client.request(:get, 'http://example.com/')
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
                end
         | 
| 122 | 
            +
              end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              describe '#perform' do
         | 
| 125 | 
            +
                let(:client) { described_class.new }
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                it 'calls finish_response before actual performance' do
         | 
| 128 | 
            +
                  TCPSocket.stub(:open) { throw :halt }
         | 
| 129 | 
            +
                  expect(client).to receive(:finish_response)
         | 
| 130 | 
            +
                  catch(:halt) { client.head "http://127.0.0.1:#{ExampleService::PORT}/" }
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                it 'calls finish_response once body was fully flushed' do
         | 
| 134 | 
            +
                  expect(client).to receive(:finish_response).twice.and_call_original
         | 
| 135 | 
            +
                  client.get("http://127.0.0.1:#{ExampleService::PORT}/").to_s
         | 
| 136 | 
            +
                end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                context 'with HEAD request' do
         | 
| 139 | 
            +
                  it 'does not iterates through body' do
         | 
| 140 | 
            +
                    expect(client).to_not receive(:readpartial)
         | 
| 141 | 
            +
                    client.head("http://127.0.0.1:#{ExampleService::PORT}/")
         | 
| 142 | 
            +
                  end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  it 'finishes response after headers were received' do
         | 
| 145 | 
            +
                    expect(client).to receive(:finish_response).twice.and_call_original
         | 
| 146 | 
            +
                    client.head("http://127.0.0.1:#{ExampleService::PORT}/")
         | 
| 147 | 
            +
                  end
         | 
| 148 | 
            +
                end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                context 'when server fully flushes response in one chunk' do
         | 
| 151 | 
            +
                  before do
         | 
| 152 | 
            +
                    socket_spy = double
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    chunks = [
         | 
| 155 | 
            +
                      <<-RESPONSE.gsub(/^\s*\| */, '').gsub(/\n/, "\r\n")
         | 
| 156 | 
            +
                      | HTTP/1.1 200 OK
         | 
| 157 | 
            +
                      | Content-Type: text/html
         | 
| 158 | 
            +
                      | Server: WEBrick/1.3.1 (Ruby/1.9.3/2013-11-22)
         | 
| 159 | 
            +
                      | Date: Mon, 24 Mar 2014 00:32:22 GMT
         | 
| 160 | 
            +
                      | Content-Length: 15
         | 
| 161 | 
            +
                      | Connection: Keep-Alive
         | 
| 162 | 
            +
                      |
         | 
| 163 | 
            +
                      | <!doctype html>
         | 
| 164 | 
            +
                      RESPONSE
         | 
| 165 | 
            +
                    ]
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    socket_spy.stub(:close) { nil }
         | 
| 168 | 
            +
                    socket_spy.stub(:closed?) { true }
         | 
| 169 | 
            +
                    socket_spy.stub(:readpartial) { chunks.shift }
         | 
| 170 | 
            +
                    socket_spy.stub(:<<) { nil }
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                    TCPSocket.stub(:open) { socket_spy }
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  it 'properly reads body' do
         | 
| 176 | 
            +
                    body = client.get("http://127.0.0.1:#{ExampleService::PORT}/").to_s
         | 
| 177 | 
            +
                    expect(body).to eq '<!doctype html>'
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
              end
         | 
| 181 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::ContentType do
         | 
| 4 | 
            +
              describe '.parse' do
         | 
| 5 | 
            +
                context 'with text/plain' do
         | 
| 6 | 
            +
                  subject { described_class.parse 'text/plain' }
         | 
| 7 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 8 | 
            +
                  its(:charset)   { should be_nil }
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                context 'with tEXT/plaIN' do
         | 
| 12 | 
            +
                  subject { described_class.parse 'tEXT/plaIN' }
         | 
| 13 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 14 | 
            +
                  its(:charset)   { should be_nil }
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                context 'with text/plain; charset=utf-8' do
         | 
| 18 | 
            +
                  subject { described_class.parse 'text/plain; charset=utf-8' }
         | 
| 19 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 20 | 
            +
                  its(:charset)   { should eq 'utf-8' }
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                context 'with text/plain; charset="utf-8"' do
         | 
| 24 | 
            +
                  subject { described_class.parse 'text/plain; charset="utf-8"' }
         | 
| 25 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 26 | 
            +
                  its(:charset)   { should eq 'utf-8' }
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                context 'with text/plain; charSET=utf-8' do
         | 
| 30 | 
            +
                  subject { described_class.parse 'text/plain; charSET=utf-8' }
         | 
| 31 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 32 | 
            +
                  its(:charset)   { should eq 'utf-8' }
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                context 'with text/plain; foo=bar; charset=utf-8' do
         | 
| 36 | 
            +
                  subject { described_class.parse 'text/plain; foo=bar; charset=utf-8' }
         | 
| 37 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 38 | 
            +
                  its(:charset)   { should eq 'utf-8' }
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                context 'with text/plain;charset=utf-8;foo=bar' do
         | 
| 42 | 
            +
                  subject { described_class.parse 'text/plain;charset=utf-8;foo=bar' }
         | 
| 43 | 
            +
                  its(:mime_type) { should eq 'text/plain' }
         | 
| 44 | 
            +
                  its(:charset)   { should eq 'utf-8' }
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::Headers::Mixin do
         | 
| 4 | 
            +
              let :dummy_class do
         | 
| 5 | 
            +
                Class.new do
         | 
| 6 | 
            +
                  include HTTP::Headers::Mixin
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(headers)
         | 
| 9 | 
            +
                    @headers = headers
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              let(:headers) { HTTP::Headers.new }
         | 
| 15 | 
            +
              let(:dummy)   { dummy_class.new headers }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              describe '#headers' do
         | 
| 18 | 
            +
                it 'returns @headers instance variable' do
         | 
| 19 | 
            +
                  expect(dummy.headers).to be headers
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              describe '#[]' do
         | 
| 24 | 
            +
                it 'proxies to headers#[]' do
         | 
| 25 | 
            +
                  expect(headers).to receive(:[]).with(:accept)
         | 
| 26 | 
            +
                  dummy[:accept]
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              describe '#[]=' do
         | 
| 31 | 
            +
                it 'proxies to headers#[]' do
         | 
| 32 | 
            +
                  expect(headers).to receive(:[]=).with(:accept, 'text/plain')
         | 
| 33 | 
            +
                  dummy[:accept] = 'text/plain'
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,417 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            describe HTTP::Headers do
         | 
| 4 | 
            +
              subject(:headers) { described_class.new }
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              it 'is Enumerable' do
         | 
| 7 | 
            +
                expect(headers).to be_an Enumerable
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              describe '#set' do
         | 
| 11 | 
            +
                it 'sets header value' do
         | 
| 12 | 
            +
                  headers.set 'Accept', 'application/json'
         | 
| 13 | 
            +
                  expect(headers['Accept']).to eq 'application/json'
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it 'normalizes header name' do
         | 
| 17 | 
            +
                  headers.set :content_type, 'application/json'
         | 
| 18 | 
            +
                  expect(headers['Content-Type']).to eq 'application/json'
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                it 'overwrites previous value' do
         | 
| 22 | 
            +
                  headers.set :set_cookie, 'hoo=ray'
         | 
| 23 | 
            +
                  headers.set :set_cookie, 'woo=hoo'
         | 
| 24 | 
            +
                  expect(headers['Set-Cookie']).to eq 'woo=hoo'
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                it 'allows set multiple values' do
         | 
| 28 | 
            +
                  headers.set :set_cookie, 'hoo=ray'
         | 
| 29 | 
            +
                  headers.set :set_cookie, %w[hoo=ray woo=hoo]
         | 
| 30 | 
            +
                  expect(headers['Set-Cookie']).to eq %w[hoo=ray woo=hoo]
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              describe '#[]=' do
         | 
| 35 | 
            +
                it 'sets header value' do
         | 
| 36 | 
            +
                  headers['Accept'] = 'application/json'
         | 
| 37 | 
            +
                  expect(headers['Accept']).to eq 'application/json'
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                it 'normalizes header name' do
         | 
| 41 | 
            +
                  headers[:content_type] = 'application/json'
         | 
| 42 | 
            +
                  expect(headers['Content-Type']).to eq 'application/json'
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                it 'overwrites previous value' do
         | 
| 46 | 
            +
                  headers[:set_cookie] = 'hoo=ray'
         | 
| 47 | 
            +
                  headers[:set_cookie] = 'woo=hoo'
         | 
| 48 | 
            +
                  expect(headers['Set-Cookie']).to eq 'woo=hoo'
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                it 'allows set multiple values' do
         | 
| 52 | 
            +
                  headers[:set_cookie] = 'hoo=ray'
         | 
| 53 | 
            +
                  headers[:set_cookie] = %w[hoo=ray woo=hoo]
         | 
| 54 | 
            +
                  expect(headers['Set-Cookie']).to eq %w[hoo=ray woo=hoo]
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              describe '#delete' do
         | 
| 59 | 
            +
                before { headers.set 'Content-Type', 'application/json' }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                it 'removes given header' do
         | 
| 62 | 
            +
                  headers.delete 'Content-Type'
         | 
| 63 | 
            +
                  expect(headers['Content-Type']).to be_nil
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                it 'normalizes header name' do
         | 
| 67 | 
            +
                  headers.delete :content_type
         | 
| 68 | 
            +
                  expect(headers['Content-Type']).to be_nil
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
              describe '#add' do
         | 
| 73 | 
            +
                it 'sets header value' do
         | 
| 74 | 
            +
                  headers.add 'Accept', 'application/json'
         | 
| 75 | 
            +
                  expect(headers['Accept']).to eq 'application/json'
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                it 'normalizes header name' do
         | 
| 79 | 
            +
                  headers.add :content_type, 'application/json'
         | 
| 80 | 
            +
                  expect(headers['Content-Type']).to eq 'application/json'
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                it 'appends new value if header exists' do
         | 
| 84 | 
            +
                  headers.add :set_cookie, 'hoo=ray'
         | 
| 85 | 
            +
                  headers.add :set_cookie, 'woo=hoo'
         | 
| 86 | 
            +
                  expect(headers['Set-Cookie']).to eq %w[hoo=ray woo=hoo]
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                it 'allows append multiple values' do
         | 
| 90 | 
            +
                  headers.add :set_cookie, 'hoo=ray'
         | 
| 91 | 
            +
                  headers.add :set_cookie, %w[woo=hoo yup=pie]
         | 
| 92 | 
            +
                  expect(headers['Set-Cookie']).to eq %w[hoo=ray woo=hoo yup=pie]
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              describe '#get' do
         | 
| 97 | 
            +
                before { headers.set 'Content-Type', 'application/json' }
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                it 'returns array of associated values' do
         | 
| 100 | 
            +
                  expect(headers.get 'Content-Type').to eq %w[application/json]
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                it 'normalizes header name' do
         | 
| 104 | 
            +
                  expect(headers.get :content_type).to eq %w[application/json]
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                context 'when header does not exists' do
         | 
| 108 | 
            +
                  it 'returns empty array' do
         | 
| 109 | 
            +
                    expect(headers.get :accept).to eq []
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
              end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
              describe '#[]' do
         | 
| 115 | 
            +
                context 'when header does not exists' do
         | 
| 116 | 
            +
                  it 'returns nil' do
         | 
| 117 | 
            +
                    expect(headers[:accept]).to be_nil
         | 
| 118 | 
            +
                  end
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                context 'when header has a single value' do
         | 
| 122 | 
            +
                  before { headers.set 'Content-Type', 'application/json' }
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                  it 'normalizes header name' do
         | 
| 125 | 
            +
                    expect(headers[:content_type]).to_not be_nil
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
             | 
| 128 | 
            +
                  it 'returns it returns a single value' do
         | 
| 129 | 
            +
                    expect(headers[:content_type]).to eq 'application/json'
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                context 'when header has a multiple values' do
         | 
| 134 | 
            +
                  before do
         | 
| 135 | 
            +
                    headers.add :set_cookie, 'hoo=ray'
         | 
| 136 | 
            +
                    headers.add :set_cookie, 'woo=hoo'
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  it 'normalizes header name' do
         | 
| 140 | 
            +
                    expect(headers[:set_cookie]).to_not be_nil
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                  it 'returns array of associated values' do
         | 
| 144 | 
            +
                    expect(headers[:set_cookie]).to eq %w[hoo=ray woo=hoo]
         | 
| 145 | 
            +
                  end
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
              end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
              describe '#to_h' do
         | 
| 150 | 
            +
                before do
         | 
| 151 | 
            +
                  headers.add :content_type, 'application/json'
         | 
| 152 | 
            +
                  headers.add :set_cookie,   'hoo=ray'
         | 
| 153 | 
            +
                  headers.add :set_cookie,   'woo=hoo'
         | 
| 154 | 
            +
                end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                it 'returns a Hash' do
         | 
| 157 | 
            +
                  expect(headers.to_h).to be_a Hash
         | 
| 158 | 
            +
                end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                it 'returns Hash with normalized keys' do
         | 
| 161 | 
            +
                  expect(headers.to_h.keys).to match_array %w[Content-Type Set-Cookie]
         | 
| 162 | 
            +
                end
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                context 'for a header with single value' do
         | 
| 165 | 
            +
                  it 'provides a value as is' do
         | 
| 166 | 
            +
                    expect(headers.to_h['Content-Type']).to eq 'application/json'
         | 
| 167 | 
            +
                  end
         | 
| 168 | 
            +
                end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                context 'for a header with multiple values' do
         | 
| 171 | 
            +
                  it 'provides an array of values' do
         | 
| 172 | 
            +
                    expect(headers.to_h['Set-Cookie']).to eq %w[hoo=ray woo=hoo]
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
                end
         | 
| 175 | 
            +
              end
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              describe '#to_a' do
         | 
| 178 | 
            +
                before do
         | 
| 179 | 
            +
                  headers.add :content_type, 'application/json'
         | 
| 180 | 
            +
                  headers.add :set_cookie,   'hoo=ray'
         | 
| 181 | 
            +
                  headers.add :set_cookie,   'woo=hoo'
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                it 'returns an Array' do
         | 
| 185 | 
            +
                  expect(headers.to_a).to be_a Array
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                it 'returns Array of key/value pairs with normalized keys' do
         | 
| 189 | 
            +
                  expect(headers.to_a).to eq [
         | 
| 190 | 
            +
                    %w[Content-Type application/json],
         | 
| 191 | 
            +
                    %w[Set-Cookie hoo=ray],
         | 
| 192 | 
            +
                    %w[Set-Cookie woo=hoo]
         | 
| 193 | 
            +
                  ]
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
              end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
              describe '#inspect' do
         | 
| 198 | 
            +
                before  { headers.set :set_cookie, %w[hoo=ray woo=hoo] }
         | 
| 199 | 
            +
                subject { headers.inspect }
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                it { should eq '#<HTTP::Headers {"Set-Cookie"=>["hoo=ray", "woo=hoo"]}>' }
         | 
| 202 | 
            +
              end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
              describe '#keys' do
         | 
| 205 | 
            +
                before do
         | 
| 206 | 
            +
                  headers.add :content_type, 'application/json'
         | 
| 207 | 
            +
                  headers.add :set_cookie,   'hoo=ray'
         | 
| 208 | 
            +
                  headers.add :set_cookie,   'woo=hoo'
         | 
| 209 | 
            +
                end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                it 'returns uniq keys only' do
         | 
| 212 | 
            +
                  expect(headers.keys).to have_exactly(2).items
         | 
| 213 | 
            +
                end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                it 'normalizes keys' do
         | 
| 216 | 
            +
                  expect(headers.keys).to include('Content-Type', 'Set-Cookie')
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
              end
         | 
| 219 | 
            +
             | 
| 220 | 
            +
              describe '#each' do
         | 
| 221 | 
            +
                before do
         | 
| 222 | 
            +
                  headers.add :set_cookie,   'hoo=ray'
         | 
| 223 | 
            +
                  headers.add :content_type, 'application/json'
         | 
| 224 | 
            +
                  headers.add :set_cookie,   'woo=hoo'
         | 
| 225 | 
            +
                end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                it 'yields each key/value pair separatedly' do
         | 
| 228 | 
            +
                  expect { |b| headers.each(&b) }.to yield_control.exactly(3).times
         | 
| 229 | 
            +
                end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                it 'yields headers in the same order they were added' do
         | 
| 232 | 
            +
                  expect { |b| headers.each(&b) }.to yield_successive_args(
         | 
| 233 | 
            +
                    %w[Set-Cookie hoo=ray],
         | 
| 234 | 
            +
                    %w[Content-Type application/json],
         | 
| 235 | 
            +
                    %w[Set-Cookie woo=hoo]
         | 
| 236 | 
            +
                  )
         | 
| 237 | 
            +
                end
         | 
| 238 | 
            +
              end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
              describe '.empty?' do
         | 
| 241 | 
            +
                subject { headers.empty? }
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                context 'initially' do
         | 
| 244 | 
            +
                  it { should be_true }
         | 
| 245 | 
            +
                end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                context 'when header exists' do
         | 
| 248 | 
            +
                  before { headers.add :accept, 'text/plain' }
         | 
| 249 | 
            +
                  it { should be_false }
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                context 'when last header was removed' do
         | 
| 253 | 
            +
                  before do
         | 
| 254 | 
            +
                    headers.add :accept, 'text/plain'
         | 
| 255 | 
            +
                    headers.delete :accept
         | 
| 256 | 
            +
                  end
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                  it { should be_true }
         | 
| 259 | 
            +
                end
         | 
| 260 | 
            +
              end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
              describe '#hash' do
         | 
| 263 | 
            +
                let(:left)  { described_class.new }
         | 
| 264 | 
            +
                let(:right) { described_class.new }
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                it 'equals if two headers equals' do
         | 
| 267 | 
            +
                  left.add :accept, 'text/plain'
         | 
| 268 | 
            +
                  right.add :accept, 'text/plain'
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                  expect(left.hash).to eq right.hash
         | 
| 271 | 
            +
                end
         | 
| 272 | 
            +
              end
         | 
| 273 | 
            +
             | 
| 274 | 
            +
              describe '#==' do
         | 
| 275 | 
            +
                let(:left)  { described_class.new }
         | 
| 276 | 
            +
                let(:right) { described_class.new }
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                it 'compares header keys and values' do
         | 
| 279 | 
            +
                  left.add :accept, 'text/plain'
         | 
| 280 | 
            +
                  right.add :accept, 'text/plain'
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                  expect(left).to eq right
         | 
| 283 | 
            +
                end
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                it 'allows comparison with Array of key/value pairs' do
         | 
| 286 | 
            +
                  left.add :accept, 'text/plain'
         | 
| 287 | 
            +
                  expect(left).to eq [%w[Accept text/plain]]
         | 
| 288 | 
            +
                end
         | 
| 289 | 
            +
             | 
| 290 | 
            +
                it 'sensitive to headers order' do
         | 
| 291 | 
            +
                  left.add :accept, 'text/plain'
         | 
| 292 | 
            +
                  left.add :cookie, 'woo=hoo'
         | 
| 293 | 
            +
                  right.add :cookie, 'woo=hoo'
         | 
| 294 | 
            +
                  right.add :accept, 'text/plain'
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                  expect(left).to_not eq right
         | 
| 297 | 
            +
                end
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                it 'sensitive to header values order' do
         | 
| 300 | 
            +
                  left.add :cookie, 'hoo=ray'
         | 
| 301 | 
            +
                  left.add :cookie, 'woo=hoo'
         | 
| 302 | 
            +
                  right.add :cookie, 'woo=hoo'
         | 
| 303 | 
            +
                  right.add :cookie, 'hoo=ray'
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                  expect(left).to_not eq right
         | 
| 306 | 
            +
                end
         | 
| 307 | 
            +
              end
         | 
| 308 | 
            +
             | 
| 309 | 
            +
              describe '#dup' do
         | 
| 310 | 
            +
                before { headers.set :content_type, 'application/json' }
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                subject(:dupped) { headers.dup }
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                it { should be_a described_class }
         | 
| 315 | 
            +
                it { should_not be headers }
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                it 'has headers copied' do
         | 
| 318 | 
            +
                  expect(dupped[:content_type]).to eq 'application/json'
         | 
| 319 | 
            +
                end
         | 
| 320 | 
            +
             | 
| 321 | 
            +
                context 'modifying a copy' do
         | 
| 322 | 
            +
                  before { dupped.set :content_type, 'text/plain' }
         | 
| 323 | 
            +
             | 
| 324 | 
            +
                  it 'modifies dupped copy' do
         | 
| 325 | 
            +
                    expect(dupped[:content_type]).to eq 'text/plain'
         | 
| 326 | 
            +
                  end
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                  it 'does not affects original headers' do
         | 
| 329 | 
            +
                    expect(headers[:content_type]).to eq 'application/json'
         | 
| 330 | 
            +
                  end
         | 
| 331 | 
            +
                end
         | 
| 332 | 
            +
              end
         | 
| 333 | 
            +
             | 
| 334 | 
            +
              describe '#merge!' do
         | 
| 335 | 
            +
                before do
         | 
| 336 | 
            +
                  headers.set :host, 'example.com'
         | 
| 337 | 
            +
                  headers.set :accept, 'application/json'
         | 
| 338 | 
            +
                  headers.merge! :accept => 'plain/text', :cookie => %w[hoo=ray woo=hoo]
         | 
| 339 | 
            +
                end
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                it 'leaves headers not presented in other as is' do
         | 
| 342 | 
            +
                  expect(headers[:host]).to eq 'example.com'
         | 
| 343 | 
            +
                end
         | 
| 344 | 
            +
             | 
| 345 | 
            +
                it 'overwrites existing values' do
         | 
| 346 | 
            +
                  expect(headers[:accept]).to eq 'plain/text'
         | 
| 347 | 
            +
                end
         | 
| 348 | 
            +
             | 
| 349 | 
            +
                it 'appends other headers, not presented in base' do
         | 
| 350 | 
            +
                  expect(headers[:cookie]).to eq %w[hoo=ray woo=hoo]
         | 
| 351 | 
            +
                end
         | 
| 352 | 
            +
              end
         | 
| 353 | 
            +
             | 
| 354 | 
            +
              describe '#merge' do
         | 
| 355 | 
            +
                before do
         | 
| 356 | 
            +
                  headers.set :host, 'example.com'
         | 
| 357 | 
            +
                  headers.set :accept, 'application/json'
         | 
| 358 | 
            +
                end
         | 
| 359 | 
            +
             | 
| 360 | 
            +
                subject(:merged) do
         | 
| 361 | 
            +
                  headers.merge :accept => 'plain/text', :cookie => %w[hoo=ray woo=hoo]
         | 
| 362 | 
            +
                end
         | 
| 363 | 
            +
             | 
| 364 | 
            +
                it { should be_a described_class }
         | 
| 365 | 
            +
                it { should_not be headers }
         | 
| 366 | 
            +
             | 
| 367 | 
            +
                it 'does not affects original headers' do
         | 
| 368 | 
            +
                  expect(merged.to_h).to_not eq headers.to_h
         | 
| 369 | 
            +
                end
         | 
| 370 | 
            +
             | 
| 371 | 
            +
                it 'leaves headers not presented in other as is' do
         | 
| 372 | 
            +
                  expect(merged[:host]).to eq 'example.com'
         | 
| 373 | 
            +
                end
         | 
| 374 | 
            +
             | 
| 375 | 
            +
                it 'overwrites existing values' do
         | 
| 376 | 
            +
                  expect(merged[:accept]).to eq 'plain/text'
         | 
| 377 | 
            +
                end
         | 
| 378 | 
            +
             | 
| 379 | 
            +
                it 'appends other headers, not presented in base' do
         | 
| 380 | 
            +
                  expect(merged[:cookie]).to eq %w[hoo=ray woo=hoo]
         | 
| 381 | 
            +
                end
         | 
| 382 | 
            +
              end
         | 
| 383 | 
            +
             | 
| 384 | 
            +
              describe '.coerce' do
         | 
| 385 | 
            +
                let(:dummyClass) { Class.new { def respond_to?(*); end } }
         | 
| 386 | 
            +
             | 
| 387 | 
            +
                it 'accepts any object that respond to #to_hash' do
         | 
| 388 | 
            +
                  hashie = double :to_hash => {'accept' => 'json'}
         | 
| 389 | 
            +
                  expect(described_class.coerce(hashie)['accept']).to eq 'json'
         | 
| 390 | 
            +
                end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                it 'accepts any object that respond to #to_h' do
         | 
| 393 | 
            +
                  hashie = double :to_h => {'accept' => 'json'}
         | 
| 394 | 
            +
                  expect(described_class.coerce(hashie)['accept']).to eq 'json'
         | 
| 395 | 
            +
                end
         | 
| 396 | 
            +
             | 
| 397 | 
            +
                it 'accepts any object that respond to #to_a' do
         | 
| 398 | 
            +
                  hashie = double :to_a => [%w[accept json]]
         | 
| 399 | 
            +
                  expect(described_class.coerce(hashie)['accept']).to eq 'json'
         | 
| 400 | 
            +
                end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                it 'fails if given object cannot be coerced' do
         | 
| 403 | 
            +
                  expect { described_class.coerce dummyClass.new }.to raise_error HTTP::Error
         | 
| 404 | 
            +
                end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                context 'with duplicate header keys (mixed case)' do
         | 
| 407 | 
            +
                  let(:headers) { {'Set-Cookie' => 'hoo=ray', 'set-cookie' => 'woo=hoo'} }
         | 
| 408 | 
            +
             | 
| 409 | 
            +
                  it 'adds all headers' do
         | 
| 410 | 
            +
                    expect(described_class.coerce(headers).to_a).to match_array([
         | 
| 411 | 
            +
                      %w[Set-Cookie hoo=ray],
         | 
| 412 | 
            +
                      %w[Set-Cookie woo=hoo]
         | 
| 413 | 
            +
                    ])
         | 
| 414 | 
            +
                  end
         | 
| 415 | 
            +
                end
         | 
| 416 | 
            +
              end
         | 
| 417 | 
            +
            end
         |