rest-client 1.8.0 → 2.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.
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
- 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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
- Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY
14
- 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
- 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
- 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
- mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
18
- 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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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
- MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ
6
- 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
- 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
- Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS
9
- 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
- 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
- NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe
12
- 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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-----
@@ -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