rest-client 1.8.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/.mailmap +10 -0
  4. data/.rspec +2 -1
  5. data/.rubocop +2 -0
  6. data/.rubocop-disables.yml +386 -0
  7. data/.rubocop.yml +8 -0
  8. data/.travis.yml +56 -8
  9. data/AUTHORS +26 -1
  10. data/README.md +901 -0
  11. data/Rakefile +27 -3
  12. data/bin/restclient +3 -5
  13. data/history.md +181 -0
  14. data/lib/restclient/abstract_response.rb +172 -55
  15. data/lib/restclient/exceptions.rb +96 -55
  16. data/lib/restclient/params_array.rb +72 -0
  17. data/lib/restclient/payload.rb +70 -74
  18. data/lib/restclient/platform.rb +19 -0
  19. data/lib/restclient/raw_response.rb +21 -7
  20. data/lib/restclient/request.rb +540 -281
  21. data/lib/restclient/resource.rb +19 -9
  22. data/lib/restclient/response.rb +75 -6
  23. data/lib/restclient/utils.rb +274 -0
  24. data/lib/restclient/version.rb +2 -1
  25. data/lib/restclient.rb +21 -3
  26. data/rest-client.gemspec +12 -10
  27. data/spec/ISS.jpg +0 -0
  28. data/spec/helpers.rb +54 -0
  29. data/spec/integration/_lib.rb +1 -0
  30. data/spec/integration/capath_digicert/3513523f.0 +22 -0
  31. data/spec/integration/capath_digicert/399e7759.0 +22 -0
  32. data/spec/integration/capath_digicert/digicert.crt +20 -17
  33. data/spec/integration/certs/digicert.crt +20 -17
  34. data/spec/integration/httpbin_spec.rb +128 -0
  35. data/spec/integration/integration_spec.rb +97 -14
  36. data/spec/integration/request_spec.rb +25 -2
  37. data/spec/spec_helper.rb +28 -1
  38. data/spec/unit/_lib.rb +1 -0
  39. data/spec/unit/abstract_response_spec.rb +95 -38
  40. data/spec/unit/exceptions_spec.rb +41 -28
  41. data/spec/unit/params_array_spec.rb +36 -0
  42. data/spec/unit/payload_spec.rb +118 -68
  43. data/spec/unit/raw_response_spec.rb +10 -6
  44. data/spec/unit/request2_spec.rb +34 -12
  45. data/spec/unit/request_spec.rb +745 -424
  46. data/spec/unit/resource_spec.rb +31 -27
  47. data/spec/unit/response_spec.rb +134 -57
  48. data/spec/unit/restclient_spec.rb +16 -15
  49. data/spec/unit/utils_spec.rb +147 -0
  50. data/spec/unit/windows/root_certs_spec.rb +3 -3
  51. metadata +79 -29
  52. data/README.rdoc +0 -324
  53. data/spec/integration/capath_digicert/244b5494.0 +0 -19
  54. data/spec/integration/capath_digicert/81b9768f.0 +0 -19
  55. data/spec/unit/master_shake.jpg +0 -0
@@ -1,19 +1,22 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG
3
- 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
- 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
- MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
6
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
19
22
  -----END CERTIFICATE-----
@@ -1,19 +1,22 @@
1
1
  -----BEGIN CERTIFICATE-----
2
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB
13
- 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
- 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
- V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp
16
- 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
- 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
- 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
19
22
  -----END CERTIFICATE-----
@@ -0,0 +1,128 @@
1
+ require_relative '_lib'
2
+ require 'json'
3
+
4
+ require 'zlib'
5
+
6
+ describe RestClient::Request do
7
+ before(:all) do
8
+ WebMock.disable!
9
+ end
10
+
11
+ after(:all) do
12
+ WebMock.enable!
13
+ end
14
+
15
+ def default_httpbin_url
16
+ # add a hack to work around java/jruby bug
17
+ # java.lang.RuntimeException: Could not generate DH keypair with backtrace
18
+ # Also (2017-04-09) Travis Jruby versions have a broken CA keystore
19
+ if ENV['TRAVIS_RUBY_VERSION'] =~ /\Ajruby-/
20
+ 'http://httpbin.org/'
21
+ else
22
+ 'https://httpbin.org/'
23
+ end
24
+ end
25
+
26
+ def httpbin(suffix='')
27
+ url = ENV.fetch('HTTPBIN_URL', default_httpbin_url)
28
+ unless url.end_with?('/')
29
+ url += '/'
30
+ end
31
+
32
+ url + suffix
33
+ end
34
+
35
+ def execute_httpbin(suffix, opts={})
36
+ opts = {url: httpbin(suffix)}.merge(opts)
37
+ RestClient::Request.execute(opts)
38
+ end
39
+
40
+ def execute_httpbin_json(suffix, opts={})
41
+ JSON.parse(execute_httpbin(suffix, opts))
42
+ end
43
+
44
+ describe '.execute' do
45
+ it 'sends a user agent' do
46
+ data = execute_httpbin_json('user-agent', method: :get)
47
+ expect(data['user-agent']).to match(/rest-client/)
48
+ end
49
+
50
+ it 'receives cookies on 302' do
51
+ expect {
52
+ execute_httpbin('cookies/set?foo=bar', method: :get, max_redirects: 0)
53
+ }.to raise_error(RestClient::Found) { |ex|
54
+ expect(ex.http_code).to eq 302
55
+ expect(ex.response.cookies['foo']).to eq 'bar'
56
+ }
57
+ end
58
+
59
+ it 'passes along cookies through 302' do
60
+ data = execute_httpbin_json('cookies/set?foo=bar', method: :get)
61
+ expect(data).to have_key('cookies')
62
+ expect(data['cookies']['foo']).to eq 'bar'
63
+ end
64
+
65
+ it 'handles quote wrapped cookies' do
66
+ expect {
67
+ execute_httpbin('cookies/set?foo=' + CGI.escape('"bar:baz"'),
68
+ method: :get, max_redirects: 0)
69
+ }.to raise_error(RestClient::Found) { |ex|
70
+ expect(ex.http_code).to eq 302
71
+ expect(ex.response.cookies['foo']).to eq '"bar:baz"'
72
+ }
73
+ end
74
+
75
+ it 'sends basic auth' do
76
+ user = 'user'
77
+ pass = 'pass'
78
+
79
+ data = execute_httpbin_json("basic-auth/#{user}/#{pass}", method: :get, user: user, password: pass)
80
+ expect(data).to eq({'authenticated' => true, 'user' => user})
81
+
82
+ expect {
83
+ execute_httpbin_json("basic-auth/#{user}/#{pass}", method: :get, user: user, password: 'badpass')
84
+ }.to raise_error(RestClient::Unauthorized) { |ex|
85
+ expect(ex.http_code).to eq 401
86
+ }
87
+ end
88
+
89
+ it 'handles gzipped/deflated responses' do
90
+ [['gzip', 'gzipped'], ['deflate', 'deflated']].each do |encoding, var|
91
+ raw = execute_httpbin(encoding, method: :get)
92
+
93
+ begin
94
+ data = JSON.parse(raw)
95
+ rescue StandardError
96
+ puts "Failed to parse: " + raw.inspect
97
+ raise
98
+ end
99
+
100
+ expect(data['method']).to eq 'GET'
101
+ expect(data.fetch(var)).to be true
102
+ end
103
+ end
104
+
105
+ it 'does not uncompress response when accept-encoding is set' do
106
+ # == gzip ==
107
+ raw = execute_httpbin('gzip', method: :get, headers: {accept_encoding: 'gzip, deflate'})
108
+
109
+ # check for gzip magic number
110
+ expect(raw.body).to start_with("\x1F\x8B".b)
111
+
112
+ decoded = Zlib::GzipReader.new(StringIO.new(raw.body)).read
113
+ parsed = JSON.parse(decoded)
114
+
115
+ expect(parsed['method']).to eq 'GET'
116
+ expect(parsed.fetch('gzipped')).to be true
117
+
118
+ # == delate ==
119
+ raw = execute_httpbin('deflate', method: :get, headers: {accept_encoding: 'gzip, deflate'})
120
+
121
+ decoded = Zlib::Inflate.new.inflate(raw.body)
122
+ parsed = JSON.parse(decoded)
123
+
124
+ expect(parsed['method']).to eq 'GET'
125
+ expect(parsed.fetch('deflated')).to be true
126
+ end
127
+ end
128
+ end
@@ -1,4 +1,6 @@
1
- require 'spec_helper'
1
+ # -*- coding: utf-8 -*-
2
+ require_relative '_lib'
3
+ require 'base64'
2
4
 
3
5
  describe RestClient do
4
6
 
@@ -6,15 +8,8 @@ describe RestClient do
6
8
  body = 'abc'
7
9
  stub_request(:get, "www.example.com").to_return(:body => body, :status => 200)
8
10
  response = RestClient.get "www.example.com"
9
- response.code.should eq 200
10
- response.body.should eq body
11
- end
12
-
13
- it "a simple request with gzipped content" do
14
- stub_request(:get, "www.example.com").with(:headers => { 'Accept-Encoding' => 'gzip, deflate' }).to_return(:body => "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000", :status => 200, :headers => { 'Content-Encoding' => 'gzip' } )
15
- response = RestClient.get "www.example.com"
16
- response.code.should eq 200
17
- response.body.should eq "i'm gziped\n"
11
+ expect(response.code).to eq 200
12
+ expect(response.body).to eq body
18
13
  end
19
14
 
20
15
  it "a 404" do
@@ -24,12 +19,100 @@ describe RestClient do
24
19
  RestClient.get "www.example.com"
25
20
  raise
26
21
  rescue RestClient::ResourceNotFound => e
27
- e.http_code.should eq 404
28
- e.response.code.should eq 404
29
- e.response.body.should eq body
30
- e.http_body.should eq body
22
+ expect(e.http_code).to eq 404
23
+ expect(e.response.code).to eq 404
24
+ expect(e.response.body).to eq body
25
+ expect(e.http_body).to eq body
31
26
  end
32
27
  end
33
28
 
29
+ describe 'charset parsing' do
30
+ it 'handles utf-8' do
31
+ body = "λ".force_encoding('ASCII-8BIT')
32
+ stub_request(:get, "www.example.com").to_return(
33
+ :body => body, :status => 200, :headers => {
34
+ 'Content-Type' => 'text/plain; charset=UTF-8'
35
+ })
36
+ response = RestClient.get "www.example.com"
37
+ expect(response.encoding).to eq Encoding::UTF_8
38
+ expect(response.valid_encoding?).to eq true
39
+ end
40
+
41
+ it 'handles windows-1252' do
42
+ body = "\xff".force_encoding('ASCII-8BIT')
43
+ stub_request(:get, "www.example.com").to_return(
44
+ :body => body, :status => 200, :headers => {
45
+ 'Content-Type' => 'text/plain; charset=windows-1252'
46
+ })
47
+ response = RestClient.get "www.example.com"
48
+ expect(response.encoding).to eq Encoding::WINDOWS_1252
49
+ expect(response.encode('utf-8')).to eq "ÿ"
50
+ expect(response.valid_encoding?).to eq true
51
+ end
52
+
53
+ it 'handles binary' do
54
+ body = "\xfe".force_encoding('ASCII-8BIT')
55
+ stub_request(:get, "www.example.com").to_return(
56
+ :body => body, :status => 200, :headers => {
57
+ 'Content-Type' => 'application/octet-stream; charset=binary'
58
+ })
59
+ response = RestClient.get "www.example.com"
60
+ expect(response.encoding).to eq Encoding::BINARY
61
+ expect {
62
+ response.encode('utf-8')
63
+ }.to raise_error(Encoding::UndefinedConversionError)
64
+ expect(response.valid_encoding?).to eq true
65
+ end
66
+
67
+ it 'handles euc-jp' do
68
+ body = "\xA4\xA2\xA4\xA4\xA4\xA6\xA4\xA8\xA4\xAA".
69
+ force_encoding(Encoding::BINARY)
70
+ body_utf8 = 'あいうえお'
71
+ expect(body_utf8.encoding).to eq Encoding::UTF_8
72
+
73
+ stub_request(:get, 'www.example.com').to_return(
74
+ :body => body, :status => 200, :headers => {
75
+ 'Content-Type' => 'text/plain; charset=EUC-JP'
76
+ })
77
+ response = RestClient.get 'www.example.com'
78
+ expect(response.encoding).to eq Encoding::EUC_JP
79
+ expect(response.valid_encoding?).to eq true
80
+ expect(response.length).to eq 5
81
+ expect(response.encode('utf-8')).to eq body_utf8
82
+ end
34
83
 
84
+ it 'defaults to the default encoding' do
85
+ stub_request(:get, 'www.example.com').to_return(
86
+ body: 'abc', status: 200, headers: {
87
+ 'Content-Type' => 'text/plain'
88
+ })
89
+
90
+ response = RestClient.get 'www.example.com'
91
+ # expect(response.encoding).to eq Encoding.default_external
92
+ expect(response.encoding).to eq Encoding::UTF_8
93
+ end
94
+
95
+ it 'handles invalid encoding' do
96
+ stub_request(:get, 'www.example.com').to_return(
97
+ body: 'abc', status: 200, headers: {
98
+ 'Content-Type' => 'text; charset=plain'
99
+ })
100
+
101
+ response = RestClient.get 'www.example.com'
102
+ # expect(response.encoding).to eq Encoding.default_external
103
+ expect(response.encoding).to eq Encoding::UTF_8
104
+ end
105
+
106
+ it 'leaves images as binary' do
107
+ gif = Base64.strict_decode64('R0lGODlhAQABAAAAADs=')
108
+
109
+ stub_request(:get, 'www.example.com').to_return(
110
+ body: gif, status: 200, headers: {
111
+ 'Content-Type' => 'image/gif'
112
+ })
113
+
114
+ response = RestClient.get 'www.example.com'
115
+ expect(response.encoding).to eq Encoding::BINARY
116
+ end
117
+ end
35
118
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require_relative '_lib'
2
2
 
3
3
  describe RestClient::Request do
4
4
  before(:all) do
@@ -75,7 +75,7 @@ describe RestClient::Request do
75
75
  },
76
76
  )
77
77
  expect {request.execute }.to_not raise_error
78
- ran_callback.should eq(true)
78
+ expect(ran_callback).to eq(true)
79
79
  end
80
80
 
81
81
  it "fails verification when the callback returns false",
@@ -101,4 +101,27 @@ describe RestClient::Request do
101
101
  expect { request.execute }.to_not raise_error
102
102
  end
103
103
  end
104
+
105
+ describe "timeouts" do
106
+ it "raises OpenTimeout when it hits an open timeout" do
107
+ request = RestClient::Request.new(
108
+ :method => :get,
109
+ :url => 'http://www.mozilla.org',
110
+ :open_timeout => 1e-10,
111
+ )
112
+ expect { request.execute }.to(
113
+ raise_error(RestClient::Exceptions::OpenTimeout))
114
+ end
115
+
116
+ it "raises ReadTimeout when it hits a read timeout via :read_timeout" do
117
+ request = RestClient::Request.new(
118
+ :method => :get,
119
+ :url => 'https://www.mozilla.org',
120
+ :read_timeout => 1e-10,
121
+ )
122
+ expect { request.execute }.to(
123
+ raise_error(RestClient::Exceptions::ReadTimeout))
124
+ end
125
+ end
126
+
104
127
  end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,29 @@
1
1
  require 'webmock/rspec'
2
- require 'restclient'
2
+ require 'rest-client'
3
+
4
+ require_relative './helpers'
5
+
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.raise_errors_for_deprecations!
9
+
10
+ # Run specs in random order to surface order dependencies. If you find an
11
+ # order dependency and want to debug it, you can fix the order by providing
12
+ # the seed, which is printed after each run.
13
+ # --seed 1234
14
+ config.order = 'random'
15
+
16
+ # always run with ruby warnings enabled
17
+ # TODO: figure out why this is so obscenely noisy (rspec bug?)
18
+ # config.warnings = true
19
+
20
+ # add helpers
21
+ config.include Helpers, :include_helpers
22
+
23
+ config.mock_with :rspec do |mocks|
24
+ mocks.yield_receiver_to_any_instance_implementation_blocks = true
25
+ end
26
+ end
27
+
28
+ # always run with ruby warnings enabled (see above)
29
+ $VERBOSE = true
data/spec/unit/_lib.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative '../spec_helper'
@@ -1,88 +1,145 @@
1
- require 'spec_helper'
1
+ require_relative '_lib'
2
2
 
3
- describe RestClient::AbstractResponse do
3
+ describe RestClient::AbstractResponse, :include_helpers do
4
4
 
5
+ # Sample class implementing AbstractResponse used for testing.
5
6
  class MyAbstractResponse
6
7
 
7
8
  include RestClient::AbstractResponse
8
9
 
9
10
  attr_accessor :size
10
11
 
11
- def initialize net_http_res, args, request
12
- @net_http_res = net_http_res
13
- @args = args
14
- @request = request
12
+ def initialize(net_http_res, request)
13
+ response_set_vars(net_http_res, request, Time.now - 1)
15
14
  end
16
15
 
17
16
  end
18
17
 
19
18
  before do
20
- @net_http_res = double('net http response')
21
- @request = double('restclient request', :url => 'http://example.com')
22
- @response = MyAbstractResponse.new(@net_http_res, {}, @request)
19
+ @net_http_res = res_double()
20
+ @request = request_double(url: 'http://example.com', method: 'get')
21
+ @response = MyAbstractResponse.new(@net_http_res, @request)
23
22
  end
24
23
 
25
24
  it "fetches the numeric response code" do
26
- @net_http_res.should_receive(:code).and_return('200')
27
- @response.code.should eq 200
25
+ expect(@net_http_res).to receive(:code).and_return('200')
26
+ expect(@response.code).to eq 200
28
27
  end
29
28
 
30
29
  it "has a nice description" do
31
- @net_http_res.should_receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
32
- @net_http_res.should_receive(:code).and_return('200')
33
- @response.description.should eq "200 OK | application/pdf bytes\n"
30
+ expect(@net_http_res).to receive(:to_hash).and_return({'Content-Type' => ['application/pdf']})
31
+ expect(@net_http_res).to receive(:code).and_return('200')
32
+ expect(@response.description).to eq "200 OK | application/pdf bytes\n"
34
33
  end
35
34
 
36
- it "beautifies the headers by turning the keys to symbols" do
37
- h = RestClient::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
38
- h.keys.first.should eq :content_type
39
- end
35
+ describe '.beautify_headers' do
36
+ it "beautifies the headers by turning the keys to symbols" do
37
+ h = RestClient::AbstractResponse.beautify_headers('content-type' => [ 'x' ])
38
+ expect(h.keys.first).to eq :content_type
39
+ end
40
40
 
41
- it "beautifies the headers by turning the values to strings instead of one-element arrays" do
42
- h = RestClient::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
43
- h.values.first.should eq 'text/html'
41
+ it "beautifies the headers by turning the values to strings instead of one-element arrays" do
42
+ h = RestClient::AbstractResponse.beautify_headers('x' => [ 'text/html' ] )
43
+ expect(h.values.first).to eq 'text/html'
44
+ end
45
+
46
+ it 'joins multiple header values by comma' do
47
+ expect(RestClient::AbstractResponse.beautify_headers(
48
+ {'My-Header' => ['one', 'two']}
49
+ )).to eq({:my_header => 'one, two'})
50
+ end
51
+
52
+ it 'leaves set-cookie headers as array' do
53
+ expect(RestClient::AbstractResponse.beautify_headers(
54
+ {'Set-Cookie' => ['cookie1=foo', 'cookie2=bar']}
55
+ )).to eq({:set_cookie => ['cookie1=foo', 'cookie2=bar']})
56
+ end
44
57
  end
45
58
 
46
59
  it "fetches the headers" do
47
- @net_http_res.should_receive(:to_hash).and_return('content-type' => [ 'text/html' ])
48
- @response.headers.should eq({ :content_type => 'text/html' })
60
+ expect(@net_http_res).to receive(:to_hash).and_return('content-type' => [ 'text/html' ])
61
+ expect(@response.headers).to eq({ :content_type => 'text/html' })
49
62
  end
50
63
 
51
64
  it "extracts cookies from response headers" do
52
- @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
53
- @response.cookies.should eq({ 'session_id' => '1' })
65
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
66
+ expect(@response.cookies).to eq({ 'session_id' => '1' })
54
67
  end
55
68
 
56
69
  it "extract strange cookies" do
57
- @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
58
- @response.headers.should eq({:set_cookie => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/']})
59
- @response.cookies.should eq({ 'session_id' => 'ZJ/HQVH6YE+rVkTpn0zvTQ==' })
70
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/'])
71
+ expect(@response.headers).to eq({:set_cookie => ['session_id=ZJ/HQVH6YE+rVkTpn0zvTQ==; path=/']})
72
+ expect(@response.cookies).to eq({ 'session_id' => 'ZJ/HQVH6YE+rVkTpn0zvTQ==' })
60
73
  end
61
74
 
62
75
  it "doesn't escape cookies" do
63
- @net_http_res.should_receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
64
- @response.cookies.should eq({ 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' })
76
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca; path=/'])
77
+ expect(@response.cookies).to eq({ 'session_id' => 'BAh7BzoNYXBwX25hbWUiEGFwcGxpY2F0aW9uOgpsb2dpbiIKYWRtaW4%3D%0A--08114ba654f17c04d20dcc5228ec672508f738ca' })
78
+ end
79
+
80
+ describe '.cookie_jar' do
81
+ it 'extracts cookies into cookie jar' do
82
+ expect(@net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
83
+ expect(@response.cookie_jar).to be_a HTTP::CookieJar
84
+
85
+ cookie = @response.cookie_jar.cookies.first
86
+ expect(cookie.domain).to eq 'example.com'
87
+ expect(cookie.name).to eq 'session_id'
88
+ expect(cookie.value).to eq '1'
89
+ expect(cookie.path).to eq '/'
90
+ end
91
+
92
+ it 'handles cookies when URI scheme is implicit' do
93
+ net_http_res = double('net http response')
94
+ expect(net_http_res).to receive(:to_hash).and_return('set-cookie' => ['session_id=1; path=/'])
95
+ request = double('request', url: 'example.com', uri: URI.parse('http://example.com'),
96
+ method: 'get', cookie_jar: HTTP::CookieJar.new, redirection_history: nil)
97
+ response = MyAbstractResponse.new(net_http_res, request)
98
+ expect(response.cookie_jar).to be_a HTTP::CookieJar
99
+
100
+ cookie = response.cookie_jar.cookies.first
101
+ expect(cookie.domain).to eq 'example.com'
102
+ expect(cookie.name).to eq 'session_id'
103
+ expect(cookie.value).to eq '1'
104
+ expect(cookie.path).to eq '/'
105
+ end
65
106
  end
66
107
 
67
108
  it "can access the net http result directly" do
68
- @response.net_http_res.should eq @net_http_res
109
+ expect(@response.net_http_res).to eq @net_http_res
69
110
  end
70
111
 
71
112
  describe "#return!" do
72
113
  it "should return the response itself on 200-codes" do
73
- @net_http_res.should_receive(:code).and_return('200')
74
- @response.return!.should be_equal(@response)
114
+ expect(@net_http_res).to receive(:code).and_return('200')
115
+ expect(@response.return!).to be_equal(@response)
75
116
  end
76
117
 
77
118
  it "should raise RequestFailed on unknown codes" do
78
- @net_http_res.should_receive(:code).and_return('1000')
79
- lambda { @response.return! }.should raise_error RestClient::RequestFailed
119
+ expect(@net_http_res).to receive(:code).and_return('1000')
120
+ expect { @response.return! }.to raise_error RestClient::RequestFailed
80
121
  end
81
122
 
82
123
  it "should raise an error on a redirection after non-GET/HEAD requests" do
83
- @net_http_res.should_receive(:code).and_return('301')
84
- @response.args.merge(:method => :put)
85
- lambda { @response.return! }.should raise_error RestClient::RequestFailed
124
+ expect(@net_http_res).to receive(:code).and_return('301')
125
+ expect(@request).to receive(:method).and_return('put')
126
+ expect(@response).not_to receive(:follow_redirection)
127
+ expect { @response.return! }.to raise_error RestClient::RequestFailed
128
+ end
129
+
130
+ it "should follow 302 redirect" do
131
+ expect(@net_http_res).to receive(:code).and_return('302')
132
+ expect(@response).to receive(:check_max_redirects).and_return('fake-check')
133
+ expect(@response).to receive(:follow_redirection).and_return('fake-redirection')
134
+ expect(@response.return!).to eq 'fake-redirection'
135
+ end
136
+
137
+ it "should gracefully handle 302 redirect with no location header" do
138
+ @net_http_res = res_double(code: 302)
139
+ @request = request_double()
140
+ @response = MyAbstractResponse.new(@net_http_res, @request)
141
+ expect(@response).to receive(:check_max_redirects).and_return('fake-check')
142
+ expect { @response.return! }.to raise_error RestClient::Found
86
143
  end
87
144
  end
88
145
  end