diff --git a/Gemfile b/Gemfile index 39770f4..4d8cd4b 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ gem 'curb', '~> 0.8', :require => false, :platforms => :ruby gem 'em-http-request', :require => false, :platforms => [:ruby, :jruby] gem 'em-synchrony', :require => false, :platforms => [:ruby, :jruby] gem 'excon', '~> 0.21', :require => false, :platforms => [:ruby, :jruby] -gem 'net-http-persistent', '~> 2.8', :require => false +gem 'net-http-persistent', :require => false gem 'http', :require => false # coverage diff --git a/httpi.gemspec b/httpi.gemspec index 5e448a7..f32e338 100644 --- a/httpi.gemspec +++ b/httpi.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.add_development_dependency 'rubyntlm', '~> 0.3.2' s.add_development_dependency 'rake', '~> 10.0' - s.add_development_dependency 'rspec', '~> 2.14' + s.add_development_dependency 'rspec', '~> 3.5' s.add_development_dependency 'mocha', '~> 0.13' s.add_development_dependency 'puma', '~> 2.3.2' s.add_development_dependency 'webmock' diff --git a/lib/httpi/adapter/curb.rb b/lib/httpi/adapter/curb.rb index f6afb41..5e9ef43 100644 --- a/lib/httpi/adapter/curb.rb +++ b/lib/httpi/adapter/curb.rb @@ -115,6 +115,7 @@ def setup_ssl_auth @client.cert_key = ssl.cert_key_file @client.cert = ssl.cert_file @client.certpassword = ssl.cert_key_password + @client.set(:ssl_cipher_list, ssl.ciphers.join(':')) if ssl.ciphers @client.ssl_verify_peer = ssl.verify_mode == :peer end diff --git a/lib/httpi/adapter/excon.rb b/lib/httpi/adapter/excon.rb index d384def..2e5bfd8 100644 --- a/lib/httpi/adapter/excon.rb +++ b/lib/httpi/adapter/excon.rb @@ -71,6 +71,7 @@ def client_opts opts[:ssl_verify_peer] = false end + opts[:ciphers] = ssl.ciphers if ssl.ciphers opts[:ssl_version] = ssl.ssl_version if ssl.ssl_version opts diff --git a/lib/httpi/adapter/http.rb b/lib/httpi/adapter/http.rb index e60e787..f9b5188 100644 --- a/lib/httpi/adapter/http.rb +++ b/lib/httpi/adapter/http.rb @@ -59,6 +59,7 @@ def create_client context.key = @request.auth.ssl.cert_key context.ssl_version = @request.auth.ssl.ssl_version if @request.auth.ssl.ssl_version != nil context.verify_mode = @request.auth.ssl.openssl_verify_mode + context.ciphers = @request.auth.ssl.ciphers if @request.auth.ssl.ciphers client = ::HTTP::Client.new(:ssl_context => context) else diff --git a/lib/httpi/adapter/httpclient.rb b/lib/httpi/adapter/httpclient.rb index 97cd56b..3dd7b2e 100644 --- a/lib/httpi/adapter/httpclient.rb +++ b/lib/httpi/adapter/httpclient.rb @@ -72,6 +72,7 @@ def setup_ssl_auth # Send client-side certificate regardless of state of SSL verify mode @client.ssl_config.client_cert = ssl.cert @client.ssl_config.client_key = ssl.cert_key + @client.ssl_config.ciphers = ssl.ciphers if ssl.ciphers @client.ssl_config.verify_mode = ssl.openssl_verify_mode end diff --git a/lib/httpi/adapter/net_http.rb b/lib/httpi/adapter/net_http.rb index 6390178..673462a 100644 --- a/lib/httpi/adapter/net_http.rb +++ b/lib/httpi/adapter/net_http.rb @@ -166,6 +166,7 @@ def setup_ssl_auth # Send client-side certificate regardless of state of SSL verify mode @client.key = ssl.cert_key @client.cert = ssl.cert + @client.ciphers = ssl.ciphers if ssl.ciphers @client.verify_mode = ssl.openssl_verify_mode end diff --git a/lib/httpi/adapter/net_http_persistent.rb b/lib/httpi/adapter/net_http_persistent.rb index 554cc4e..1eeee25 100644 --- a/lib/httpi/adapter/net_http_persistent.rb +++ b/lib/httpi/adapter/net_http_persistent.rb @@ -12,7 +12,11 @@ class NetHTTPPersistent < NetHTTP private def create_client - Net::HTTP::Persistent.new thread_key + if Gem::Version.new(Net::HTTP::Persistent::VERSION) < Gem::Version.new('3.0.0') + Net::HTTP::Persistent.new thread_key + else + Net::HTTP::Persistent.new name: thread_key + end end def perform(http, http_request, &on_body) diff --git a/lib/httpi/auth/ssl.rb b/lib/httpi/auth/ssl.rb index 2241e00..3c389ce 100644 --- a/lib/httpi/auth/ssl.rb +++ b/lib/httpi/auth/ssl.rb @@ -34,9 +34,29 @@ def present? # Accessor for the ca_path to validate SSL certificates. attr_accessor :ca_cert_path - # ertificate store holds trusted CA certificates used to verify peer certificates. + # Certificate store holds trusted CA certificates used to verify peer certificates. attr_accessor :cert_store + # Returns the available symmetric algorithms for encryption and decryption. + # @!attribute ciphers + # @return [, nil] + attr_reader :ciphers + + # Sets the available symmetric algorithms for encryption and decryption. + # @see OpenSSL::SSL::SSLContext#ciphers + # @example + # ssl.ciphers = "cipher1:cipher2:..." + # ssl.ciphers = [name, ...] + # ssl.ciphers = [[name, version, bits, alg_bits], ...] + def ciphers=(ciphers) + @ciphers = + if ciphers + context = OpenSSL::SSL::SSLContext.new + context.ciphers = ciphers + context.ciphers.map(&:first) + end + end + # Returns the cert type to validate SSL certificates PEM|DER. def cert_type @cert_type ||= :pem diff --git a/spec/httpi/adapter/curb_spec.rb b/spec/httpi/adapter/curb_spec.rb index d47a28e..9798c56 100644 --- a/spec/httpi/adapter/curb_spec.rb +++ b/spec/httpi/adapter/curb_spec.rb @@ -280,6 +280,7 @@ request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem" request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem" request.auth.ssl.cert_key_password = 'example' + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers request end diff --git a/spec/httpi/adapter/excon_spec.rb b/spec/httpi/adapter/excon_spec.rb index 52af8c2..27ba2a5 100644 --- a/spec/httpi/adapter/excon_spec.rb +++ b/spec/httpi/adapter/excon_spec.rb @@ -93,6 +93,7 @@ it "works when no client cert is specified" do request = HTTPI::Request.new(@server.url) request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers response = HTTPI.get(request, adapter) expect(response.body).to eq("get") @@ -103,6 +104,7 @@ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem" request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem" + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers response = HTTPI.get(request, adapter) expect(response.body).to eq("get") @@ -114,6 +116,7 @@ request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file request.auth.ssl.cert = OpenSSL::X509::Certificate.new File.open("spec/fixtures/client_cert.pem").read request.auth.ssl.cert_key = OpenSSL::PKey.read File.open("spec/fixtures/client_key.pem").read + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers response = HTTPI.get(request, adapter) expect(response.body).to eq("get") diff --git a/spec/httpi/adapter/http_spec.rb b/spec/httpi/adapter/http_spec.rb index 34b482d..7070466 100644 --- a/spec/httpi/adapter/http_spec.rb +++ b/spec/httpi/adapter/http_spec.rb @@ -91,6 +91,7 @@ it "works when set up properly" do request = HTTPI::Request.new(@server.url) request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers response = HTTPI.get(request, adapter) expect(response.body).to eq("get") diff --git a/spec/httpi/adapter/httpclient_spec.rb b/spec/httpi/adapter/httpclient_spec.rb index 41ca299..7622638 100644 --- a/spec/httpi/adapter/httpclient_spec.rb +++ b/spec/httpi/adapter/httpclient_spec.rb @@ -133,12 +133,14 @@ before do request.auth.ssl.cert_key_file = "spec/fixtures/client_key.pem" request.auth.ssl.cert_file = "spec/fixtures/client_cert.pem" + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers end it "send certificate regardless of state of SSL verify mode" do request.auth.ssl.verify_mode = :none ssl_config.expects(:client_cert=).with(request.auth.ssl.cert) ssl_config.expects(:client_key=).with(request.auth.ssl.cert_key) + ssl_config.expects(:ciphers=).with(request.auth.ssl.ciphers) adapter.request(:get) end @@ -147,6 +149,7 @@ ssl_config.expects(:client_cert=).with(request.auth.ssl.cert) ssl_config.expects(:client_key=).with(request.auth.ssl.cert_key) ssl_config.expects(:verify_mode=).with(request.auth.ssl.openssl_verify_mode) + ssl_config.expects(:ciphers=).with(request.auth.ssl.ciphers) adapter.request(:get) end diff --git a/spec/httpi/adapter/net_http_persistent_spec.rb b/spec/httpi/adapter/net_http_persistent_spec.rb index 8f02ec8..1e2febb 100644 --- a/spec/httpi/adapter/net_http_persistent_spec.rb +++ b/spec/httpi/adapter/net_http_persistent_spec.rb @@ -87,6 +87,10 @@ request = HTTPI::Request.new(@server.url) request.auth.ssl.ca_cert_file = IntegrationServer.ssl_ca_file + if Gem::Version.new(Net::HTTP::Persistent::VERSION) >= Gem::Version.new('3.0.0') + request.auth.ssl.ciphers = OpenSSL::Cipher.ciphers + end + response = HTTPI.get(request, adapter) expect(response.body).to eq("get") end diff --git a/spec/httpi/auth/ssl_spec.rb b/spec/httpi/auth/ssl_spec.rb index 62c33e3..3a2ff3d 100644 --- a/spec/httpi/auth/ssl_spec.rb +++ b/spec/httpi/auth/ssl_spec.rb @@ -158,6 +158,23 @@ end end + describe '#ciphers' do + subject { ssl.ciphers } + let(:ssl) { HTTPI::Auth::SSL.new } + + context 'without ciphers' do + before { ssl.ciphers = nil } + + it { is_expected.to eq(nil) } + end + + context 'with ciphers' do + before { ssl.ciphers = OpenSSL::Cipher.ciphers } + + it { is_expected.to be_any.and(all(be_an_instance_of(String))) } + end + end + def ssl ssl = HTTPI::Auth::SSL.new ssl.cert_key_file = "spec/fixtures/client_key.pem"