8000 Add ipaddr optional parameter to Net::HTTP#start · github/ruby@54072e3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 54072e3

Browse files
committed
Add ipaddr optional parameter to Net::HTTP#start
to replace the address for TCP/IP connection [Feature ruby#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 ruby#1893 closes ruby#1977
1 parent 1943279 commit 54072e3

File tree

3 files changed

+57
-8
lines changed

3 files changed

+57
-8
lines changed

NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,11 @@ Net::FTP::
508508
* Add Net::FTP#features to check available features, and Net::FTP#option to
509509
enable/disable each of them. [Feature #15964]
510510

511+
Net::HTTP::
512+
513+
* Add ipaddr optional parameter to Net::HTTP#start to replace the address for
514+
TCP/IP connection [Feature #5180]
515+
511516
Net::IMAP::
512517

513518
* Add Server Name Indication (SNI) support. [Feature #15594]

lib/net/http.rb

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ def HTTP.socket_type #:nodoc: obsolete
571571
# _opt_ :: optional hash
572572
#
573573
# _opt_ sets following values by its accessor.
574-
# The keys are ca_file, ca_path, cert, cert_store, ciphers,
574+
# The keys are ipaddr, ca_file, ca_path, cert, cert_store, ciphers,
575575
# close_on_empty_response, key, open_timeout, read_timeout, write_timeout, ssl_timeout,
576576
# ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
577577
# If you set :use_ssl as true, you can use https and default value of
@@ -590,6 +590,7 @@ def HTTP.start(address, *arg, &block) # :yield: +http+
590590
p_addr = :ENV if arg.size < 2
591591
port = https_default_port if !port && opt && opt[:use_ssl]
592592
http = new(address, port, p_addr, p_port, p_user, p_pass)
593+
http.ipaddr = opt[:ipaddr] if opt[:ipaddr]
593594

594595
if opt
595596
if opt[:use_ssl]
@@ -660,6 +661,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p
660661
def initialize(address, port = nil)
661662
@address = address
662663
@port = (port || HTTP.default_port)
664+
@ipaddr = nil
663665
@local_host = nil
664666
@local_port = nil
665667
@curr_http_version = HTTPVersion
@@ -727,6 +729,17 @@ def set_debug_output(output)
727729
attr_writer :proxy_user
728730
attr_writer :proxy_pass
729731

732+
# The IP address to connect to/used to connect to
733+
def ipaddr
734+
started? ? @socket.io.peeraddr[3] : @ipaddr
735+
end
736+
737+
# Set the IP address to connect to
738+
def ipaddr=(addr)
739+
raise IOError, "ipaddr value changed, but session already started" if started?
740+
@ipaddr = addr
741+
end
742+
730743
# Number of seconds to wait for the connection to open. Any number
731744
# may be used, including Floats for fractional seconds. If the HTTP
732745
# object cannot open a connection in this many seconds, it raises a
@@ -934,20 +947,20 @@ def do_start
934947

935948
def connect
936949
if proxy? then
937-
conn_address = proxy_address
950+
conn_addr = proxy_address
938951
conn_port = proxy_port
939952
else
940-
conn_address = address
953+
conn_addr = conn_address
941954
conn_port = port
942955
end
943956

944-
D "opening connection to #{conn_address}:#{conn_port}..."
957+
D "opening connection to #{conn_addr}:#{conn_port}..."
945958
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) {
946959
begin
947-
TCPSocket.open(conn_address, conn_port, @local_host, @local_port)
960+
TCPSocket.open(conn_addr, conn_port, @local_host, @local_port)
948961
rescue => e
949962
raise e, "Failed to open TCP connection to " +
950-
"#{conn_address}:#{conn_port} (#{e.message})"
963+
"#{conn_addr}:#{conn_port} (#{e.message})"
951964
end
952965
}
953966
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -984,7 +997,7 @@ def connect
984997
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
985998
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
986999
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
987-
D "starting SSL for #{conn_address}:#{conn_port}..."
1000+
D "starting SSL for #{conn_addr}:#{conn_port}..."
9881001
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
9891002
s.sync_close = true
9901003
# Server Name Indication (SNI) RFC 3546
@@ -1161,7 +1174,7 @@ def proxy_pass
11611174
# without proxy, obsolete
11621175

11631176
def conn_address # :nodoc:
1164-
address()
1177+
@ipaddr || address()
11651178
end
11661179

11671180
def conn_port # :nodoc:

test/net/http/test_https.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,37 @@ def test_get
5050
skip $!
5151
end
5252

53+
def test_get_SNI
54+
http = Net::HTTP.new("localhost", config("port"))
55+
http.ipaddr = config('host')
56+
http.use_ssl = true
57+
http.cert_store = TEST_STORE
58+
certs = []
59+
http.verify_callback = Proc.new do |preverify_ok, store_ctx|
60+
certs << store_ctx.current_cert
61+
preverify_ok
62+
end
63+
http.request_get("/") {|res|
64+
assert_equal($test_net_http_data, res.body)
65+
}
66+
assert_equal(CA_CERT.to_der, certs[0].to_der)
67+
assert_equal(SERVER_CERT.to_der, certs[1].to_der)
68+
end
69+
70+
def test_get_SNI_failure
71+
http = Net::HTTP.new("invalid_servername", config("port"))
72+
http.ipaddr = config('host')
73+
http.use_ssl = true
74+
http.cert_store = TEST_STORE
75+
certs = []
76+
http.verify_callback = Proc.new do |preverify_ok, store_ctx|
77+
certs << store_ctx.current_cert
78+
preverify_ok
79+
end
80+
@log_tester = lambda {|_| }
81+
assert_raise(OpenSSL::SSL::SSLError){ http.start }
82+
end
83+
5384
def test_post
5485
http = Net::HTTP.new("localhost", config("port"))
5586
http.use_ssl = true

0 commit comments

Comments
 (0)
0