summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNARUSE, Yui <naruse@airemix.jp>2019-12-09 20:19:11 +0900
committerNARUSE, Yui <naruse@airemix.jp>2019-12-09 20:21:49 +0900
commit54072e329cab7207fba133caba4fc12b45add8f9 (patch)
tree89cc3dca25a16017330c81cf5dc13243894c2313
parent194327942690a7997c7b48d34cc105c6ec8b8d40 (diff)
Add ipaddr optional parameter to Net::HTTP#start
to replace the address for TCP/IP connection [Feature #5180] There're 3 layers of hostname: * host address for TCP/IP * TLS server name * HTTP Host header value To test DNS round robin or check server certificate from server local, people sometimes want to connect server with given IP address but keep TLS server name and HTTP Host header value. closes [Feature #15215] closes https://github.com/ruby/ruby/pull/1893 closes https://github.com/ruby/ruby/pull/1977
-rw-r--r--NEWS5
-rw-r--r--lib/net/http.rb29
-rw-r--r--test/net/http/test_https.rb31
3 files changed, 57 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index 782060a7f3..46ab24277b 100644
--- a/NEWS
+++ b/NEWS
@@ -508,6 +508,11 @@ Net::FTP::
* Add Net::FTP#features to check available features, and Net::FTP#option to
enable/disable each of them. [Feature #15964]
+Net::HTTP::
+
+ * Add ipaddr optional parameter to Net::HTTP#start to replace the address for
+ TCP/IP connection [Feature #5180]
+
Net::IMAP::
* Add Server Name Indication (SNI) support. [Feature #15594]
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 663b901a96..283263b323 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -571,7 +571,7 @@ module Net #:nodoc:
# _opt_ :: optional hash
#
# _opt_ sets following values by its accessor.
- # The keys are ca_file, ca_path, cert, cert_store, ciphers,
+ # The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers,
# close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
# ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
# If you set :use_ssl as true, you can use https and default value of
@@ -590,6 +590,7 @@ module Net #:nodoc:
p_addr = :ENV if arg.size < 2
port = https_default_port if !port && opt && opt[:use_ssl]
http = new(address, port, p_addr, p_port, p_user, p_pass)
+ http.ipaddr = opt[:ipaddr] if opt[:ipaddr]
if opt
if opt[:use_ssl]
@@ -660,6 +661,7 @@ module Net #:nodoc:
def initialize(address, port = nil)
@address = address
@port = (port || HTTP.default_port)
+ @ipaddr = nil
@local_host = nil
@local_port = nil
@curr_http_version = HTTPVersion
@@ -727,6 +729,17 @@ module Net #:nodoc:
attr_writer :proxy_user
attr_writer :proxy_pass
+ # The IP address to connect to/used to connect to
+ def ipaddr
+ started? ? @socket.io.peeraddr[3] : @ipaddr
+ end
+
+ # Set the IP address to connect to
+ def ipaddr=(addr)
+ raise IOError, "ipaddr value changed, but session already started" if started?
+ @ipaddr = addr
+ end
+
# Number of seconds to wait for the connection to open. Any number
# may be used, including Floats for fractional seconds. If the HTTP
# object cannot open a connection in this many seconds, it raises a
@@ -934,20 +947,20 @@ module Net #:nodoc:
def connect
if proxy? then
- conn_address = proxy_address
+ conn_addr = proxy_address
conn_port = proxy_port
else
- conn_address = address
+ conn_addr = conn_address
conn_port = port
end
- D "opening connection to #{conn_address}:#{conn_port}..."
+ D "opening connection to #{conn_addr}:#{conn_port}..."
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
begin
- TCPSocket.open(conn_address, conn_port, @local_host, @local_port)
+ TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
rescue => e
raise e, "Failed to open TCP connection to " +
- "#{conn_address}:#{conn_port} (#{e.message})"
+ "#{conn_addr}:#{conn_port} (#{e.message})"
end
}
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -984,7 +997,7 @@ module Net #:nodoc:
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
- D "starting SSL for #{conn_address}:#{conn_port}..."
+ D "starting SSL for #{conn_addr}:#{conn_port}..."
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true
# Server Name Indication (SNI) RFC 3546
@@ -1161,7 +1174,7 @@ module Net #:nodoc:
# without proxy, obsolete
def conn_address # :nodoc:
- address()
+ @ipaddr || address()
end
def conn_port # :nodoc:
diff --git a/test/net/http/test_https.rb b/test/net/http/test_https.rb
index c1d486470a..9058387070 100644
--- a/test/net/http/test_https.rb
+++ b/test/net/http/test_https.rb
@@ -50,6 +50,37 @@ class TestNetHTTPS < Test::Unit::TestCase
skip $!
end
+ def test_get_SNI
+ http = Net::HTTP.new("localhost", config("port"))
+ http.ipaddr = config('host')
+ http.use_ssl = true
+ http.cert_store = TEST_STORE
+ certs = []
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ certs << store_ctx.current_cert
+ preverify_ok
+ end
+ http.request_get("/") {|res|
+ assert_equal($test_net_http_data, res.body)
+ }
+ assert_equal(CA_CERT.to_der, certs[0].to_der)
+ assert_equal(SERVER_CERT.to_der, certs[1].to_der)
+ end
+
+ def test_get_SNI_failure
+ http = Net::HTTP.new("invalid_servername", config("port"))
+ http.ipaddr = config('host')
+ http.use_ssl = true
+ http.cert_store = TEST_STORE
+ certs = []
+ http.verify_callback = Proc.new do |preverify_ok, store_ctx|
+ certs << store_ctx.current_cert
+ preverify_ok
+ end
+ @log_tester = lambda {|_| }
+ assert_raise(OpenSSL::SSL::SSLError){ http.start }
+ end
+
def test_post
http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true