rest-client 1.6.14 → 1.7.0.rc1

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 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