rest-client 1.6.14 → 1.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rest-client might be problematic. Click here for more details.

Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -3
  3. data/.travis.yml +12 -1
  4. data/AUTHORS +10 -4
  5. data/Gemfile +5 -1
  6. data/LICENSE +21 -0
  7. data/README.rdoc +27 -2
  8. data/Rakefile +78 -10
  9. data/bin/restclient +1 -1
  10. data/history.md +22 -16
  11. data/lib/restclient.rb +2 -8
  12. data/lib/restclient/exceptions.rb +7 -2
  13. data/lib/restclient/platform.rb +2 -1
  14. data/lib/restclient/request.rb +270 -48
  15. data/lib/restclient/response.rb +0 -2
  16. data/lib/restclient/version.rb +1 -1
  17. data/lib/restclient/windows.rb +8 -0
  18. data/lib/restclient/windows/root_certs.rb +105 -0
  19. data/rest-client.gemspec +14 -10
  20. data/rest-client.windows.gemspec +19 -0
  21. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  22. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  23. data/spec/integration/capath_verisign/README +8 -0
  24. data/spec/integration/capath_verisign/verisign.crt +14 -0
  25. data/spec/{integration_spec.rb → integration/integration_spec.rb} +2 -5
  26. data/spec/integration/request_spec.rb +46 -17
  27. data/spec/{base.rb → spec_helper.rb} +2 -3
  28. data/spec/{abstract_response_spec.rb → unit/abstract_response_spec.rb} +1 -1
  29. data/spec/{exceptions_spec.rb → unit/exceptions_spec.rb} +1 -4
  30. data/spec/{master_shake.jpg → unit/master_shake.jpg} +0 -0
  31. data/spec/{payload_spec.rb → unit/payload_spec.rb} +2 -1
  32. data/spec/{raw_response_spec.rb → unit/raw_response_spec.rb} +1 -1
  33. data/spec/{request2_spec.rb → unit/request2_spec.rb} +1 -4
  34. data/spec/{request_spec.rb → unit/request_spec.rb} +386 -9
  35. data/spec/{resource_spec.rb → unit/resource_spec.rb} +1 -4
  36. data/spec/{response_spec.rb → unit/response_spec.rb} +1 -4
  37. data/spec/{restclient_spec.rb → unit/restclient_spec.rb} +7 -1
  38. data/spec/unit/windows/root_certs_spec.rb +22 -0
  39. metadata +93 -58
  40. data/lib/restclient/net_http_ext.rb +0 -55
@@ -8,8 +8,6 @@ module RestClient
8
8
 
9
9
  attr_accessor :args, :net_http_res
10
10
 
11
- attr_writer :body
12
-
13
11
  def body
14
12
  self
15
13
  end
@@ -1,5 +1,5 @@
1
1
  module RestClient
2
- VERSION = '1.6.14' unless defined?(self::VERSION)
2
+ VERSION = '1.7.0.rc1' unless defined?(self::VERSION)
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -0,0 +1,8 @@
1
+ module RestClient
2
+ module Windows
3
+ end
4
+ end
5
+
6
+ if RestClient::Platform.windows?
7
+ require_relative './windows/root_certs'
8
+ end
@@ -0,0 +1,105 @@
1
+ require 'openssl'
2
+ require 'ffi'
3
+
4
+ # Adapted from Puppet, Copyright (c) Puppet Labs Inc,
5
+ # licensed under the Apache License, Version 2.0.
6
+ #
7
+ # https://github.com/puppetlabs/puppet/blob/bbe30e0a/lib/puppet/util/windows/root_certs.rb
8
+
9
+ # Represents a collection of trusted root certificates.
10
+ #
11
+ # @api public
12
+ class RestClient::Windows::RootCerts
13
+ include Enumerable
14
+ extend FFI::Library
15
+
16
+ typedef :ulong, :dword
17
+ typedef :uintptr_t, :handle
18
+
19
+ def initialize(roots)
20
+ @roots = roots
21
+ end
22
+
23
+ # Enumerates each root certificate.
24
+ # @yieldparam cert [OpenSSL::X509::Certificate] each root certificate
25
+ # @api public
26
+ def each
27
+ @roots.each {|cert| yield cert}
28
+ end
29
+
30
+ # Returns a new instance.
31
+ # @return [RestClient::Windows::RootCerts] object constructed from current root certificates
32
+ def self.instance
33
+ new(self.load_certs)
34
+ end
35
+
36
+ # Returns an array of root certificates.
37
+ #
38
+ # @return [Array<[OpenSSL::X509::Certificate]>] an array of root certificates
39
+ # @api private
40
+ def self.load_certs
41
+ certs = []
42
+
43
+ # This is based on a patch submitted to openssl:
44
+ # http://www.mail-archive.com/openssl-dev@openssl.org/msg26958.html
45
+ ptr = FFI::Pointer::NULL
46
+ store = CertOpenSystemStoreA(nil, "ROOT")
47
+ begin
48
+ while (ptr = CertEnumCertificatesInStore(store, ptr)) and not ptr.null?
49
+ context = CERT_CONTEXT.new(ptr)
50
+ cert_buf = context[:pbCertEncoded].read_bytes(context[:cbCertEncoded])
51
+ begin
52
+ certs << OpenSSL::X509::Certificate.new(cert_buf)
53
+ rescue => detail
54
+ warn("Failed to import root certificate: #{detail.inspect}")
55
+ end
56
+ end
57
+ ensure
58
+ CertCloseStore(store, 0)
59
+ end
60
+
61
+ certs
62
+ end
63
+
64
+ private
65
+
66
+ # typedef ULONG_PTR HCRYPTPROV_LEGACY;
67
+ # typedef void *HCERTSTORE;
68
+
69
+ class CERT_CONTEXT < FFI::Struct
70
+ layout(
71
+ :dwCertEncodingType, :dword,
72
+ :pbCertEncoded, :pointer,
73
+ :cbCertEncoded, :dword,
74
+ :pCertInfo, :pointer,
75
+ :hCertStore, :handle
76
+ )
77
+ end
78
+
79
+ # HCERTSTORE
80
+ # WINAPI
81
+ # CertOpenSystemStoreA(
82
+ # __in_opt HCRYPTPROV_LEGACY hProv,
83
+ # __in LPCSTR szSubsystemProtocol
84
+ # );
85
+ ffi_lib :crypt32
86
+ attach_function :CertOpenSystemStoreA, [:pointer, :string], :handle
87
+
88
+ # PCCERT_CONTEXT
89
+ # WINAPI
90
+ # CertEnumCertificatesInStore(
91
+ # __in HCERTSTORE hCertStore,
92
+ # __in_opt PCCERT_CONTEXT pPrevCertContext
93
+ # );
94
+ ffi_lib :crypt32
95
+ attach_function :CertEnumCertificatesInStore, [:handle, :pointer], :pointer
96
+
97
+ # BOOL
98
+ # WINAPI
99
+ # CertCloseStore(
100
+ # __in_opt HCERTSTORE hCertStore,
101
+ # __in DWORD dwFlags
102
+ # );
103
+ ffi_lib :crypt32
104
+ attach_function :CertCloseStore, [:handle, :dword], :bool
105
+ end
data/rest-client.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
- require File.expand_path("../lib/restclient/version", __FILE__)
3
+ require File.expand_path('../lib/restclient/version', __FILE__)
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'rest-client'
@@ -10,17 +10,21 @@ Gem::Specification.new do |s|
10
10
  s.license = 'MIT'
11
11
  s.email = 'rest.client@librelist.com'
12
12
  s.executables = ['restclient']
13
- s.extra_rdoc_files = ["README.rdoc", "history.md"]
14
- s.files = `git ls-files`.split("\n")
15
- s.test_files = `git ls-files -- spec/*`.split("\n")
13
+ s.extra_rdoc_files = ['README.rdoc', 'history.md']
14
+ s.files = `git ls-files -z`.split("\0")
15
+ s.test_files = `git ls-files -z spec/`.split("\0")
16
16
  s.homepage = 'https://github.com/rest-client/rest-client'
17
17
  s.summary = 'Simple HTTP and REST client for Ruby, inspired by microframework syntax for specifying actions.'
18
18
 
19
- s.add_dependency(%q<mime-types>, ["~> 1.16"])
20
- s.add_development_dependency(%q<rdoc>, [">= 2.4.2"])
21
- s.add_development_dependency(%q<rake>, ["~> 10.0"])
22
- s.add_development_dependency(%q<webmock>, ["~> 1.4"])
23
- s.add_development_dependency(%q<rspec>, ["~> 2.4"])
24
- s.add_development_dependency(%q<pry>)
19
+ s.add_development_dependency('webmock', '~> 1.4')
20
+ s.add_development_dependency('rspec', '~> 2.4')
21
+ s.add_development_dependency('pry')
22
+ s.add_development_dependency('pry-doc')
23
+ s.add_development_dependency('rdoc', '>= 2.4.2', '< 5.0')
24
+
25
+ s.add_dependency('mime-types', '~> 2.0')
26
+ s.add_dependency('netrc', '~> 0.7')
27
+
28
+ s.required_ruby_version = '>= 1.9.2'
25
29
  end
26
30
 
@@ -0,0 +1,19 @@
1
+ #
2
+ # Gemspec for Windows platforms. We can't put these in the main gemspec because
3
+ # it results in bundler platform hell when trying to build the gem.
4
+ #
5
+ # Set $BUILD_PLATFORM when calling gem build with this gemspec to build for
6
+ # Windows platforms like x86-mingw32.
7
+ #
8
+ s = eval(File.read(File.join(File.dirname(__FILE__), 'rest-client.gemspec')))
9
+
10
+ platform = ENV['BUILD_PLATFORM'] || RUBY_PLATFORM
11
+
12
+ case platform
13
+ when /(mingw32|mswin32)/
14
+ # ffi is needed for RestClient::Windows::RootCerts
15
+ s.add_dependency('ffi', '~> 1.9')
16
+ s.platform = platform
17
+ end
18
+
19
+ s
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
3
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
4
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
5
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
6
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
7
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
8
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
9
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
10
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
11
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
12
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
13
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
14
+ -----END CERTIFICATE-----
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
3
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
4
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
5
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
6
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
7
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
8
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
9
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
10
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
11
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
12
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
13
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
14
+ -----END CERTIFICATE-----
@@ -0,0 +1,8 @@
1
+ The CA path symlinks can be created by c_rehash(1ssl).
2
+
3
+ But in order for the tests to work on Windows, they have to be regular files.
4
+ You can turn them all into regular files by running this on a GNU system:
5
+
6
+ for file in $(find . -type l); do
7
+ cp -iv --remove-destination $(readlink -e $file) $file
8
+ done
@@ -0,0 +1,14 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
3
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
4
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
5
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
6
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
7
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
8
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
9
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
10
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
11
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
12
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
13
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
14
+ -----END CERTIFICATE-----
@@ -1,7 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
-
3
- require 'webmock/rspec'
4
- include WebMock::API
1
+ require 'spec_helper'
5
2
 
6
3
  describe RestClient do
7
4
 
@@ -35,4 +32,4 @@ describe RestClient do
35
32
  end
36
33
 
37
34
 
38
- end
35
+ end
@@ -1,37 +1,68 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), '../base')
1
+ require 'spec_helper'
2
2
 
3
3
  describe RestClient::Request do
4
+ before(:all) do
5
+ WebMock.disable!
6
+ end
7
+
8
+ after(:all) do
9
+ WebMock.enable!
10
+ end
11
+
4
12
  describe "ssl verification" do
5
13
  it "is successful with the correct ca_file" do
6
14
  request = RestClient::Request.new(
7
15
  :method => :get,
8
16
  :url => 'https://www.mozilla.org',
9
- :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
10
17
  :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "digicert.crt")
11
18
  )
12
19
  expect { request.execute }.to_not raise_error
13
20
  end
14
21
 
15
- # I don' think this feature is useful anymore (under 1.9.3 at the very least).
16
- #
17
- # Exceptions in verify_callback are ignored; RestClient has to catch OpenSSL::SSL::SSLError
18
- # and either re-throw it as is, or throw SSLCertificateNotVerified
19
- # based on the contents of the message field of the original exception
20
- #.
21
- # The client has to handle OpenSSL::SSL::SSLError exceptions anyway,
22
- # why make them handle both OpenSSL *AND* RestClient exceptions???
22
+ it "is successful with the correct ca_path" do
23
+ request = RestClient::Request.new(
24
+ :method => :get,
25
+ :url => 'https://www.mozilla.org',
26
+ :ssl_ca_path => File.join(File.dirname(__FILE__), "capath_digicert")
27
+ )
28
+ expect { request.execute }.to_not raise_error
29
+ end
30
+
31
+ # TODO: deprecate and remove RestClient::SSLCertificateNotVerified and just
32
+ # pass through OpenSSL::SSL::SSLError directly. See note in
33
+ # lib/restclient/request.rb.
23
34
  #
24
- # also see https://github.com/ruby/ruby/blob/trunk/ext/openssl/ossl.c#L237
25
- it "is unsuccessful with an incorrect ca_file" do
35
+ # On OS X, this test fails since Apple has patched OpenSSL to always fall
36
+ # back on the system CA store.
37
+ it "is unsuccessful with an incorrect ca_file", :unless => RestClient::Platform.mac? do
26
38
  request = RestClient::Request.new(
27
39
  :method => :get,
28
- :url => 'https://www.mozilla.com',
29
- :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
40
+ :url => 'https://www.mozilla.org',
30
41
  :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt")
31
42
  )
32
43
  expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified)
33
44
  end
34
45
 
46
+ # On OS X, this test fails since Apple has patched OpenSSL to always fall
47
+ # back on the system CA store.
48
+ it "is unsuccessful with an incorrect ca_path", :unless => RestClient::Platform.mac? do
49
+ request = RestClient::Request.new(
50
+ :method => :get,
51
+ :url => 'https://www.mozilla.org',
52
+ :ssl_ca_path => File.join(File.dirname(__FILE__), "capath_verisign")
53
+ )
54
+ expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified)
55
+ end
56
+
57
+ it "is successful using the default system cert store" do
58
+ request = RestClient::Request.new(
59
+ :method => :get,
60
+ :url => 'https://www.mozilla.org',
61
+ :verify_ssl => true,
62
+ )
63
+ expect {request.execute }.to_not raise_error
64
+ end
65
+
35
66
  it "executes the verify_callback" do
36
67
  ran_callback = false
37
68
  request = RestClient::Request.new(
@@ -42,7 +73,6 @@ describe RestClient::Request do
42
73
  ran_callback = true
43
74
  preverify_ok
44
75
  },
45
- :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "digicert.crt")
46
76
  )
47
77
  expect {request.execute }.to_not raise_error
48
78
  ran_callback.should eq(true)
@@ -55,7 +85,6 @@ describe RestClient::Request do
55
85
  :url => 'https://www.mozilla.org',
56
86
  :verify_ssl => true,
57
87
  :ssl_verify_callback => lambda { |preverify_ok, store_ctx| false },
58
- :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "digicert.crt")
59
88
  )
60
89
  expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified)
61
90
  end
@@ -67,7 +96,7 @@ describe RestClient::Request do
67
96
  :url => 'https://www.mozilla.org',
68
97
  :verify_ssl => true,
69
98
  :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt"),
70
- :ssl_verify_callback => lambda { |preverify_ok, store_ctx| true }
99
+ :ssl_verify_callback => lambda { |preverify_ok, store_ctx| true },
71
100
  )
72
101
  expect { request.execute }.to_not raise_error
73
102
  end
@@ -2,12 +2,11 @@ def is_ruby_19?
2
2
  RUBY_VERSION > '1.9'
3
3
  end
4
4
 
5
- require 'rubygems'
6
-
7
5
  begin
8
6
  require "ruby-debug"
9
7
  rescue LoadError
10
8
  # NOP, ignore
11
9
  end
12
10
 
13
- require File.dirname(__FILE__) + '/../lib/restclient'
11
+ require 'webmock/rspec'
12
+ require 'restclient'
@@ -1,4 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
1
+ require 'spec_helper'
2
2
 
3
3
  describe RestClient::AbstractResponse do
4
4
 
@@ -1,7 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
-
3
- require 'webmock/rspec'
4
- include WebMock::API
1
+ require 'spec_helper'
5
2
 
6
3
  describe RestClient::Exception do
7
4
  it "returns a 'message' equal to the class name if the message is not set, because 'message' should not be nil" do
File without changes
@@ -1,5 +1,6 @@
1
1
  # encoding: binary
2
- require File.join(File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'spec_helper'
3
4
 
4
5
  describe RestClient::Payload do
5
6
  context "A regular Payload" do
@@ -1,4 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
1
+ require 'spec_helper'
2
2
 
3
3
  describe RestClient::RawResponse do
4
4
  before do
@@ -1,7 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
-
3
- require 'webmock/rspec'
4
- include WebMock::API
1
+ require 'spec_helper'
5
2
 
6
3
  describe RestClient::Request do
7
4
 
@@ -1,7 +1,4 @@
1
- require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
-
3
- require 'webmock/rspec'
4
- include WebMock::API
1
+ require 'spec_helper'
5
2
 
6
3
  describe RestClient::Request do
7
4
  before do
@@ -18,6 +15,9 @@ describe RestClient::Request do
18
15
  @net.stub(:start).and_yield(@http)
19
16
  @net.stub(:use_ssl=)
20
17
  @net.stub(:verify_mode=)
18
+ @net.stub(:verify_callback=)
19
+ allow(@net).to receive(:ciphers=)
20
+ allow(@net).to receive(:cert_store=)
21
21
  RestClient.log = nil
22
22
  end
23
23
 
@@ -109,6 +109,57 @@ describe RestClient::Request do
109
109
  @request.make_headers({}).should eq({ 'Foo' => 'bar', 'Cookie' => 'session_id=1; user_id=someone'})
110
110
  end
111
111
 
112
+ it "does not escape or unescape cookies" do
113
+ cookie = 'Foo%20:Bar%0A~'
114
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com',
115
+ :cookies => {:test => cookie})
116
+ @request.should_receive(:default_headers).and_return({'Foo' => 'bar'})
117
+ @request.make_headers({}).should eq({
118
+ 'Foo' => 'bar',
119
+ 'Cookie' => "test=#{cookie}"
120
+ })
121
+ end
122
+
123
+ it "rejects cookie names containing invalid characters" do
124
+ # Cookie validity is something of a mess, but we should reject the worst of
125
+ # the RFC 6265 (4.1.1) prohibited characters such as control characters.
126
+
127
+ ['', 'foo=bar', 'foo;bar', "foo\nbar"].each do |cookie_name|
128
+ lambda {
129
+ RestClient::Request.new(:method => 'get', :url => 'example.com',
130
+ :cookies => {cookie_name => 'value'})
131
+ }.should raise_error(ArgumentError, /\AInvalid cookie name/)
132
+ end
133
+ end
134
+
135
+ it "rejects cookie values containing invalid characters" do
136
+ # Cookie validity is something of a mess, but we should reject the worst of
137
+ # the RFC 6265 (4.1.1) prohibited characters such as control characters.
138
+
139
+ ['foo,bar', 'foo;bar', "foo\nbar"].each do |cookie_value|
140
+ lambda {
141
+ RestClient::Request.new(:method => 'get', :url => 'example.com',
142
+ :cookies => {'test' => cookie_value})
143
+ }.should raise_error(ArgumentError, /\AInvalid cookie value/)
144
+ end
145
+ end
146
+
147
+ it "uses netrc credentials" do
148
+ URI.stub(:parse).and_return(double('uri', :user => nil, :password => nil, :host => 'example.com'))
149
+ Netrc.stub(:read).and_return('example.com' => ['a', 'b'])
150
+ @request.parse_url_with_auth('http://example.com/resource')
151
+ @request.user.should eq 'a'
152
+ @request.password.should eq 'b'
153
+ end
154
+
155
+ it "uses credentials in the url in preference to netrc" do
156
+ URI.stub(:parse).and_return(double('uri', :user => 'joe%20', :password => 'pass1', :host => 'example.com'))
157
+ Netrc.stub(:read).and_return('example.com' => ['a', 'b'])
158
+ @request.parse_url_with_auth('http://joe%20:pass1@example.com/resource')
159
+ @request.user.should eq 'joe '
160
+ @request.password.should eq 'pass1'
161
+ end
162
+
112
163
  it "determines the Net::HTTP class to instantiate by the method name" do
113
164
  @request.net_http_request_class(:put).should eq Net::HTTP::Put
114
165
  end
@@ -129,6 +180,13 @@ describe RestClient::Request do
129
180
  headers.should have_key('1')
130
181
  headers['1'].should eq '3'
131
182
  end
183
+
184
+ it "converts user headers to string before calling CGI::unescape which fails on non string values" do
185
+ @request.should_receive(:default_headers).and_return({ '1' => '2' })
186
+ headers = @request.make_headers('1' => 3)
187
+ headers.should have_key('1')
188
+ headers['1'].should eq '3'
189
+ end
132
190
  end
133
191
 
134
192
  describe "header symbols" do
@@ -218,6 +276,7 @@ describe RestClient::Request do
218
276
  describe "credentials" do
219
277
  it "sets up the credentials prior to the request" do
220
278
  @http.stub(:request)
279
+
221
280
  @request.stub(:process_result)
222
281
  @request.stub(:response_log)
223
282
 
@@ -249,6 +308,21 @@ describe RestClient::Request do
249
308
  lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
250
309
  end
251
310
 
311
+ it "catches OpenSSL::SSL::SSLError and raise it back without more informative message" do
312
+ @http.stub(:request).and_raise(OpenSSL::SSL::SSLError)
313
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(OpenSSL::SSL::SSLError)
314
+ end
315
+
316
+ it "catches Timeout::Error and raise the more informative RequestTimeout" do
317
+ @http.stub(:request).and_raise(Timeout::Error)
318
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::RequestTimeout)
319
+ end
320
+
321
+ it "catches Timeout::Error and raise the more informative RequestTimeout" do
322
+ @http.stub(:request).and_raise(Errno::ETIMEDOUT)
323
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::RequestTimeout)
324
+ end
325
+
252
326
  it "class method execute wraps constructor" do
253
327
  req = double("rest request")
254
328
  RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
@@ -351,6 +425,18 @@ describe RestClient::Request do
351
425
  end
352
426
 
353
427
  describe "timeout" do
428
+ it "does not set timeouts if not specified" do
429
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
430
+ @http.stub(:request)
431
+ @request.stub(:process_result)
432
+ @request.stub(:response_log)
433
+
434
+ @net.should_not_receive(:read_timeout=)
435
+ @net.should_not_receive(:open_timeout=)
436
+
437
+ @request.transmit(@uri, 'req', nil)
438
+ end
439
+
354
440
  it "set read_timeout" do
355
441
  @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
356
442
  @http.stub(:request)
@@ -372,6 +458,33 @@ describe RestClient::Request do
372
458
 
373
459
  @request.transmit(@uri, 'req', nil)
374
460
  end
461
+
462
+ it "disable timeout by setting it to nil" do
463
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => nil, :open_timeout => nil)
464
+ @http.stub(:request)
465
+ @request.stub(:process_result)
466
+ @request.stub(:response_log)
467
+
468
+ @net.should_receive(:read_timeout=).with(nil)
469
+ @net.should_receive(:open_timeout=).with(nil)
470
+
471
+ @request.transmit(@uri, 'req', nil)
472
+ end
473
+
474
+ it "deprecated: disable timeout by setting it to -1" do
475
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => -1, :open_timeout => -1)
476
+ @http.stub(:request)
477
+ @request.stub(:process_result)
478
+ @request.stub(:response_log)
479
+
480
+ @request.should_receive(:warn)
481
+ @net.should_receive(:read_timeout=).with(nil)
482
+
483
+ @request.should_receive(:warn)
484
+ @net.should_receive(:open_timeout=).with(nil)
485
+
486
+ @request.transmit(@uri, 'req', nil)
487
+ end
375
488
  end
376
489
 
377
490
  describe "ssl" do
@@ -384,11 +497,17 @@ describe RestClient::Request do
384
497
  @request.transmit(@uri, 'req', 'payload')
385
498
  end
386
499
 
387
- it "should default to not verifying ssl certificates" do
388
- @request.verify_ssl.should eq false
500
+ it "should default to verifying ssl certificates" do
501
+ @request.verify_ssl.should eq OpenSSL::SSL::VERIFY_PEER
502
+ end
503
+
504
+ it "should have expected values for VERIFY_PEER and VERIFY_NONE" do
505
+ OpenSSL::SSL::VERIFY_NONE.should eq(0)
506
+ OpenSSL::SSL::VERIFY_PEER.should eq(1)
389
507
  end
390
508
 
391
509
  it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
510
+ @request = RestClient::Request.new(:method => :put, :verify_ssl => false, :url => 'http://some/resource', :payload => 'payload')
392
511
  @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
393
512
  @http.stub(:request)
394
513
  @request.stub(:process_result)
@@ -405,6 +524,24 @@ describe RestClient::Request do
405
524
  @request.transmit(@uri, 'req', 'payload')
406
525
  end
407
526
 
527
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_PEER if verify_ssl is true" do
528
+ @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
529
+ @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
530
+ @http.stub(:request)
531
+ @request.stub(:process_result)
532
+ @request.stub(:response_log)
533
+ @request.transmit(@uri, 'req', 'payload')
534
+ end
535
+
536
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_PEER if verify_ssl is not given" do
537
+ @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload')
538
+ @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
539
+ @http.stub(:request)
540
+ @request.stub(:process_result)
541
+ @request.stub(:response_log)
542
+ @request.transmit(@uri, 'req', 'payload')
543
+ end
544
+
408
545
  it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
409
546
  mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
410
547
  @request = RestClient::Request.new( :method => :put,
@@ -422,6 +559,112 @@ describe RestClient::Request do
422
559
  @request.ssl_client_cert.should be(nil)
423
560
  end
424
561
 
562
+ it "should set the ssl_version if provided" do
563
+ @request = RestClient::Request.new(
564
+ :method => :put,
565
+ :url => 'https://some/resource',
566
+ :payload => 'payload',
567
+ :ssl_version => "TLSv1"
568
+ )
569
+ @net.should_receive(:ssl_version=).with("TLSv1")
570
+ @http.stub(:request)
571
+ @request.stub(:process_result)
572
+ @request.stub(:response_log)
573
+ @request.transmit(@uri, 'req', 'payload')
574
+ end
575
+
576
+ it "should not set the ssl_version if not provided" do
577
+ @request = RestClient::Request.new(
578
+ :method => :put,
579
+ :url => 'https://some/resource',
580
+ :payload => 'payload'
581
+ )
582
+ @net.should_not_receive(:ssl_version=).with("TLSv1")
583
+ @http.stub(:request)
584
+ @request.stub(:process_result)
585
+ @request.stub(:response_log)
586
+ @request.transmit(@uri, 'req', 'payload')
587
+ end
588
+
589
+ it "should set the ssl_ciphers if provided" do
590
+ ciphers = 'AESGCM:HIGH:!aNULL:!eNULL:RC4+RSA'
591
+ @request = RestClient::Request.new(
592
+ :method => :put,
593
+ :url => 'https://some/resource',
594
+ :payload => 'payload',
595
+ :ssl_ciphers => ciphers
596
+ )
597
+ @net.should_receive(:ciphers=).with(ciphers)
598
+ @http.stub(:request)
599
+ @request.stub(:process_result)
600
+ @request.stub(:response_log)
601
+ @request.transmit(@uri, 'req', 'payload')
602
+ end
603
+
604
+ it "should not set the ssl_ciphers if set to nil" do
605
+ @request = RestClient::Request.new(
606
+ :method => :put,
607
+ :url => 'https://some/resource',
608
+ :payload => 'payload',
609
+ :ssl_ciphers => nil,
610
+ )
611
+ @net.should_not_receive(:ciphers=)
612
+ @http.stub(:request)
613
+ @request.stub(:process_result)
614
+ @request.stub(:response_log)
615
+ @request.transmit(@uri, 'req', 'payload')
616
+ end
617
+
618
+ it "should override ssl_ciphers with better defaults with weak default ciphers" do
619
+ stub_const(
620
+ '::OpenSSL::SSL::SSLContext::DEFAULT_PARAMS',
621
+ {
622
+ :ssl_version=>"SSLv23",
623
+ :verify_mode=>1,
624
+ :ciphers=>"ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
625
+ :options=>-2147480577,
626
+ }
627
+ )
628
+
629
+ @request = RestClient::Request.new(
630
+ :method => :put,
631
+ :url => 'https://some/resource',
632
+ :payload => 'payload',
633
+ )
634
+
635
+ @net.should_receive(:ciphers=).with(RestClient::Request::DefaultCiphers)
636
+
637
+ @http.stub(:request)
638
+ @request.stub(:process_result)
639
+ @request.stub(:response_log)
640
+ @request.transmit(@uri, 'req', 'payload')
641
+ end
642
+
643
+ it "should not override ssl_ciphers with better defaults with different default ciphers" do
644
+ stub_const(
645
+ '::OpenSSL::SSL::SSLContext::DEFAULT_PARAMS',
646
+ {
647
+ :ssl_version=>"SSLv23",
648
+ :verify_mode=>1,
649
+ :ciphers=>"HIGH:!aNULL:!eNULL:!EXPORT:!LOW:!MEDIUM:!SSLv2",
650
+ :options=>-2147480577,
651
+ }
652
+ )
653
+
654
+ @request = RestClient::Request.new(
655
+ :method => :put,
656
+ :url => 'https://some/resource',
657
+ :payload => 'payload',
658
+ )
659
+
660
+ @net.should_not_receive(:ciphers=)
661
+
662
+ @http.stub(:request)
663
+ @request.stub(:process_result)
664
+ @request.stub(:response_log)
665
+ @request.transmit(@uri, 'req', 'payload')
666
+ end
667
+
425
668
  it "should set the ssl_client_cert if provided" do
426
669
  @request = RestClient::Request.new(
427
670
  :method => :put,
@@ -442,7 +685,7 @@ describe RestClient::Request do
442
685
  :url => 'https://some/resource',
443
686
  :payload => 'payload'
444
687
  )
445
- @net.should_not_receive(:cert=).with("whatsupdoc!")
688
+ @net.should_not_receive(:cert=)
446
689
  @http.stub(:request)
447
690
  @request.stub(:process_result)
448
691
  @request.stub(:response_log)
@@ -473,7 +716,7 @@ describe RestClient::Request do
473
716
  :url => 'https://some/resource',
474
717
  :payload => 'payload'
475
718
  )
476
- @net.should_not_receive(:key=).with("whatsupdoc!")
719
+ @net.should_not_receive(:key=)
477
720
  @http.stub(:request)
478
721
  @request.stub(:process_result)
479
722
  @request.stub(:response_log)
@@ -492,6 +735,7 @@ describe RestClient::Request do
492
735
  :ssl_ca_file => "Certificate Authority File"
493
736
  )
494
737
  @net.should_receive(:ca_file=).with("Certificate Authority File")
738
+ @net.should_not_receive(:cert_store=)
495
739
  @http.stub(:request)
496
740
  @request.stub(:process_result)
497
741
  @request.stub(:response_log)
@@ -504,12 +748,129 @@ describe RestClient::Request do
504
748
  :url => 'https://some/resource',
505
749
  :payload => 'payload'
506
750
  )
507
- @net.should_not_receive(:ca_file=).with("Certificate Authority File")
751
+ @net.should_not_receive(:ca_file=)
752
+ @http.stub(:request)
753
+ @request.stub(:process_result)
754
+ @request.stub(:response_log)
755
+ @request.transmit(@uri, 'req', 'payload')
756
+ end
757
+
758
+ it "should default to not having an ssl_ca_path" do
759
+ @request.ssl_ca_path.should be(nil)
760
+ end
761
+
762
+ it "should set the ssl_ca_path if provided" do
763
+ @request = RestClient::Request.new(
764
+ :method => :put,
765
+ :url => 'https://some/resource',
766
+ :payload => 'payload',
767
+ :ssl_ca_path => "Certificate Authority Path"
768
+ )
769
+ @net.should_receive(:ca_path=).with("Certificate Authority Path")
770
+ @net.should_not_receive(:cert_store=)
771
+ @http.stub(:request)
772
+ @request.stub(:process_result)
773
+ @request.stub(:response_log)
774
+ @request.transmit(@uri, 'req', 'payload')
775
+ end
776
+
777
+ it "should not set the ssl_ca_path if it is not provided" do
778
+ @request = RestClient::Request.new(
779
+ :method => :put,
780
+ :url => 'https://some/resource',
781
+ :payload => 'payload'
782
+ )
783
+ @net.should_not_receive(:ca_path=)
784
+ @http.stub(:request)
785
+ @request.stub(:process_result)
786
+ @request.stub(:response_log)
787
+ @request.transmit(@uri, 'req', 'payload')
788
+ end
789
+
790
+ it "should set the ssl_cert_store if provided" do
791
+ store = OpenSSL::X509::Store.new
792
+ store.set_default_paths
793
+
794
+ @request = RestClient::Request.new(
795
+ :method => :put,
796
+ :url => 'https://some/resource',
797
+ :payload => 'payload',
798
+ :ssl_cert_store => store
799
+ )
800
+ @net.should_receive(:cert_store=).with(store)
801
+ @net.should_not_receive(:ca_path=)
802
+ @net.should_not_receive(:ca_file=)
803
+ @http.stub(:request)
804
+ @request.stub(:process_result)
805
+ @request.stub(:response_log)
806
+ @request.transmit(@uri, 'req', 'payload')
807
+ end
808
+
809
+ it "should by default set the ssl_cert_store if no CA info is provided" do
810
+ @request = RestClient::Request.new(
811
+ :method => :put,
812
+ :url => 'https://some/resource',
813
+ :payload => 'payload'
814
+ )
815
+ @net.should_receive(:cert_store=)
816
+ @net.should_not_receive(:ca_path=)
817
+ @net.should_not_receive(:ca_file=)
508
818
  @http.stub(:request)
509
819
  @request.stub(:process_result)
510
820
  @request.stub(:response_log)
511
821
  @request.transmit(@uri, 'req', 'payload')
512
822
  end
823
+
824
+ it "should not set the ssl_cert_store if it is set falsy" do
825
+ @request = RestClient::Request.new(
826
+ :method => :put,
827
+ :url => 'https://some/resource',
828
+ :payload => 'payload',
829
+ :ssl_cert_store => nil,
830
+ )
831
+ @net.should_not_receive(:cert_store=)
832
+ @http.stub(:request)
833
+ @request.stub(:process_result)
834
+ @request.stub(:response_log)
835
+ @request.transmit(@uri, 'req', 'payload')
836
+ end
837
+
838
+ it "should not set the ssl_verify_callback by default" do
839
+ @request = RestClient::Request.new(
840
+ :method => :put,
841
+ :url => 'https://some/resource',
842
+ :payload => 'payload',
843
+ )
844
+ @net.should_not_receive(:verify_callback=)
845
+ @http.stub(:request)
846
+ @request.stub(:process_result)
847
+ @request.stub(:response_log)
848
+ @request.transmit(@uri, 'req', 'payload')
849
+ end
850
+
851
+ it "should set the ssl_verify_callback if passed" do
852
+ callback = lambda {}
853
+ @request = RestClient::Request.new(
854
+ :method => :put,
855
+ :url => 'https://some/resource',
856
+ :payload => 'payload',
857
+ :ssl_verify_callback => callback,
858
+ )
859
+ @net.should_receive(:verify_callback=).with(callback)
860
+
861
+ # we'll read cert_store on jruby
862
+ # https://github.com/jruby/jruby/issues/597
863
+ if RestClient::Platform.jruby?
864
+ allow(@net).to receive(:cert_store)
865
+ end
866
+
867
+ @http.stub(:request)
868
+ @request.stub(:process_result)
869
+ @request.stub(:response_log)
870
+ @request.transmit(@uri, 'req', 'payload')
871
+ end
872
+
873
+ # </ssl>
513
874
  end
514
875
 
515
876
  it "should still return a response object for 204 No Content responses" do
@@ -525,4 +886,20 @@ describe RestClient::Request do
525
886
  response.should_not be_nil
526
887
  response.code.should eq 204
527
888
  end
889
+
890
+ describe "raw response" do
891
+ it "should read the response into a binary-mode tempfile" do
892
+ @request = RestClient::Request.new(:method => "get", :url => "example.com", :raw_response => true)
893
+
894
+ tempfile = double("tempfile")
895
+ tempfile.should_receive(:binmode)
896
+ tempfile.stub(:open)
897
+ tempfile.stub(:close)
898
+ Tempfile.should_receive(:new).with("rest-client").and_return(tempfile)
899
+
900
+ net_http_res = Net::HTTPOK.new(nil, "200", "body")
901
+ net_http_res.stub(:read_body).and_return("body")
902
+ @request.fetch_body(net_http_res)
903
+ end
904
+ end
528
905
  end