summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog10
-rw-r--r--lib/net/http.rb9
-rw-r--r--lib/net/https.rb55
-rw-r--r--test/net/http/test_https.rb97
-rw-r--r--test/net/http/utils.rb19
5 files changed, 139 insertions, 51 deletions
diff --git a/ChangeLog b/ChangeLog
index a5294f62bb..68cdd98af5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+Fri Dec 21 01:20:56 2007 GOTOU Yuuzou <gotoyuzo@notwork.org>
+
+ * lib/net/http.rb (Net::HTTP#connect): use
+ OpenSSL::SSL::SSLContext.build instead of SSLContext.new (default
+ verify mode is now OpenSSL::SSL::VERIFY_PEER).
+
+ * lib/net/https.rb: SSL parameters are defined by attr_accessor.
+
+ * test/net/http/test_https.rb: add test for HTTPS features.
+
Fri Dec 21 01:11:37 2007 GOTOU Yuuzou <gotoyuzo@notwork.org>
* io.c (select_internal): should return original value.
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 4a16e118dd..9e2aeee2c4 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -575,10 +575,13 @@ module Net #:nodoc:
s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) }
D "opened"
if use_ssl?
- unless @ssl_context.verify_mode
- warn "warning: peer certificate won't be verified in this SSL session"
- @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ ssl_parameters = Hash.new
+ SSL_ATTRIBUTES.each do |name|
+ if value = instance_variable_get("@#{name}")
+ ssl_parameters[name] = value
+ end
end
+ @ssl_context = OpenSSL::SSL::SSLContext.build(ssl_parameters)
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true
end
diff --git a/lib/net/https.rb b/lib/net/https.rb
index fb329df43d..8a9245ea0e 100644
--- a/lib/net/https.rb
+++ b/lib/net/https.rb
@@ -102,70 +102,35 @@ require 'net/http'
require 'openssl'
module Net
-
class HTTP
remove_method :use_ssl?
def use_ssl?
@use_ssl
end
- alias use_ssl use_ssl? # for backward compatibility
-
# Turn on/off SSL.
# This flag must be set before starting session.
# If you change use_ssl value after session started,
# a Net::HTTP object raises IOError.
def use_ssl=(flag)
flag = (flag ? true : false)
- raise IOError, "use_ssl value changed, but session already started" \
- if started? and @use_ssl != flag
- if flag and not @ssl_context
- @ssl_context = OpenSSL::SSL::SSLContext.new
+ if started? and @use_ssl != flag
+ raise IOError, "use_ssl value changed, but session already started"
end
@use_ssl = flag
end
- def self.ssl_context_accessor(name)
- module_eval(<<-End, __FILE__, __LINE__ + 1)
- def #{name}
- return nil unless @ssl_context
- @ssl_context.#{name}
- end
-
- def #{name}=(val)
- @ssl_context ||= OpenSSL::SSL::SSLContext.new
- @ssl_context.#{name} = val
- end
- End
- end
-
- ssl_context_accessor :key
- ssl_context_accessor :cert
- ssl_context_accessor :ca_file
- ssl_context_accessor :ca_path
- ssl_context_accessor :verify_mode
- ssl_context_accessor :verify_callback
- ssl_context_accessor :verify_depth
- ssl_context_accessor :cert_store
-
- def ssl_timeout
- return nil unless @ssl_context
- @ssl_context.timeout
- end
-
- def ssl_timeout=(sec)
- raise ArgumentError, 'Net::HTTP#ssl_timeout= called but use_ssl=false' \
- unless use_ssl?
- @ssl_context ||= OpenSSL::SSL::SSLContext.new
- @ssl_context.timeout = sec
- end
-
- alias timeout= ssl_timeout= # for backward compatibility
+ SSL_ATTRIBUTES = %w(
+ ssl_version key cert ca_file ca_path cert_store ciphers
+ verify_mode verify_callback verify_depth ssl_timeout
+ )
+ attr_accessor *SSL_ATTRIBUTES
def peer_cert
- return nil if not use_ssl? or not @socket
+ if not use_ssl? or not @socket
+ return nil
+ end
@socket.io.peer_cert
end
end
-
end
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
new file mode 100644
index 0000000000..d74f10712c
--- /dev/null
+++ b/test/net/http/test_https.rb
@@ -0,0 +1,97 @@
+require "test/unit"
+begin
+ require 'net/https'
+rescue LoadError
+ # should skip this test
+end
+require 'stringio'
+require File.expand_path("utils", File.dirname(__FILE__))
+require File.expand_path("../../openssl/utils", File.dirname(__FILE__))
+
+class TestNetHTTPS < Test::Unit::TestCase
+ include TestNetHTTPUtils
+
+ subject = OpenSSL::X509::Name.parse("/DC=org/DC=ruby-lang/CN=localhost")
+ exts = [
+ ["keyUsage", "keyEncipherment,digitalSignature", true],
+ ]
+ key = OpenSSL::TestUtils::TEST_KEY_RSA1024
+ cert = OpenSSL::TestUtils.issue_cert(
+ subject, key, 1, Time.now, Time.now + 3600, exts,
+ nil, nil, OpenSSL::Digest::SHA1.new
+ )
+
+ CONFIG = {
+ 'host' => '127.0.0.1',
+ 'port' => 10081,
+ 'proxy_host' => nil,
+ 'proxy_port' => nil,
+ 'ssl_enable' => true,
+ 'ssl_certificate' => cert,
+ 'ssl_private_key' => key,
+ }
+
+ def test_get
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ end
+
+ def test_post
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ data = config('ssl_private_key').to_der
+ http.request_post("/", data) {|res|
+ assert_equal(data, res.body)
+ }
+ end
+
+ if ENV["RUBY_OPENSSL_TEST_ALL"]
+ def test_verify
+ http = Net::HTTP.new("ssl.netlab.jp", 443)
+ http.use_ssl = true
+ assert(
+ http.request_head("/"){|res| },
+ "The system may not have default CA certificate store."
+ )
+ end
+ end
+
+ def test_verify_none
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ end
+
+ def test_certificate_verify_failure
+ http = Net::HTTP.new("localhost", config("port"))
+ http.use_ssl = true
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ http.request_get("/") {|res| }
+ }
+ assert_match(/certificate verify failed/, ex.message)
+ end
+
+ def test_identity_verify_failure
+ http = Net::HTTP.new("127.0.0.1", config("port"))
+ http.use_ssl = true
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ store_ctx.current_cert.to_der == config('ssl_certificate').to_der
+ end
+ ex = assert_raise(OpenSSL::SSL::SSLError){
+ http.request_get("/") {|res| }
+ }
+ assert_match(/hostname was not match/, ex.message)
+ end
+end if defined?(OpenSSL)
diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb
index aec1967a74..766bf71c46 100644
--- a/test/net/http/utils.rb
+++ b/test/net/http/utils.rb
@@ -1,4 +1,9 @@
require 'webrick'
+begin
+ require "webrick/https"
+rescue LoadError
+ # SSL features cannot be tested
+end
require 'webrick/httpservlet/abstract'
module TestNetHTTPUtils
@@ -35,14 +40,22 @@ module TestNetHTTPUtils
end
def spawn_server
- @server = WEBrick::HTTPServer.new(
+ server_config = {
:BindAddress => config('host'),
:Port => config('port'),
:Logger => WEBrick::Log.new(NullWriter.new),
:AccessLog => [],
:ShutdownSocketWithoutClose => true,
- :ServerType => Thread
- )
+ :ServerType => Thread,
+ }
+ if defined?(OpenSSL) and config('ssl_enable')
+ server_config.update({
+ :SSLEnable => true,
+ :SSLCertificate => config('ssl_certificate'),
+ :SSLPrivateKey => config('ssl_private_key'),
+ })
+ end
+ @server = WEBrick::HTTPServer.new(server_config)
@server.mount('/', Servlet)
@server.start
n_try_max = 5