Skip to content

Commit c1310af

Browse files
committed
implement talking SSL to the proxy too
https://bugs.ruby-lang.org/issues/16482
1 parent eada37b commit c1310af

File tree

2 files changed

+68
-5
lines changed

2 files changed

+68
-5
lines changed

lib/net/http.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,7 +1066,7 @@ class << HTTP
10661066
# For proxy-defining arguments +p_addr+ through +p_no_proxy+,
10671067
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
10681068
#
1069-
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
1069+
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil, p_use_ssl = nil)
10701070
http = super address, port
10711071

10721072
if proxy_class? then # from Net::HTTP::Proxy()
@@ -1075,6 +1075,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p
10751075
http.proxy_port = @proxy_port
10761076
http.proxy_user = @proxy_user
10771077
http.proxy_pass = @proxy_pass
1078+
http.proxy_use_ssl = @proxy_use_ssl
10781079
elsif p_addr == :ENV then
10791080
http.proxy_from_env = true
10801081
else
@@ -1086,6 +1087,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p
10861087
http.proxy_port = p_port || default_port
10871088
http.proxy_user = p_user
10881089
http.proxy_pass = p_pass
1090+
http.proxy_use_ssl = p_use_ssl
10891091
end
10901092

10911093
http
@@ -1121,6 +1123,7 @@ def initialize(address, port = nil) # :nodoc:
11211123
@proxy_port = nil
11221124
@proxy_user = nil
11231125
@proxy_pass = nil
1126+
@proxy_use_ssl = nil
11241127

11251128
@use_ssl = false
11261129
@ssl_context = nil
@@ -1255,6 +1258,7 @@ def response_body_encoding=(value)
12551258
# Sets the proxy password;
12561259
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
12571260
attr_writer :proxy_pass
1261+
attr_writer :proxy_use_ssl
12581262

12591263
# Returns the IP address for the connection.
12601264
#
@@ -1614,7 +1618,13 @@ def connect
16141618
debug "opened"
16151619
if use_ssl?
16161620
if proxy?
1617-
plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
1621+
if @proxy_use_ssl
1622+
proxy_sock = OpenSSL::SSL::SSLSocket.new(s)
1623+
ssl_socket_connect(proxy_sock, @open_timeout)
1624+
else
1625+
proxy_sock = s
1626+
end
1627+
proxy_sock = BufferedIO.new(proxy_sock, read_timeout: @read_timeout,
16181628
write_timeout: @write_timeout,
16191629
continue_timeout: @continue_timeout,
16201630
debug_output: @debug_output)
@@ -1625,8 +1635,8 @@ def connect
16251635
buf << "Proxy-Authorization: Basic #{credential}\r\n"
16261636
end
16271637
buf << "\r\n"
1628-
plain_sock.write(buf)
1629-
HTTPResponse.read_new(plain_sock).value
1638+
proxy_sock.write(buf)
1639+
HTTPResponse.read_new(proxy_sock).value
16301640
# assuming nothing left in buffers after successful CONNECT response
16311641
end
16321642

@@ -1734,13 +1744,14 @@ def do_finish
17341744
@proxy_port = nil
17351745
@proxy_user = nil
17361746
@proxy_pass = nil
1747+
@proxy_use_ssl = nil
17371748

17381749
# Creates an \HTTP proxy class which behaves like \Net::HTTP, but
17391750
# performs all access via the specified proxy.
17401751
#
17411752
# This class is obsolete. You may pass these same parameters directly to
17421753
# \Net::HTTP.new. See Net::HTTP.new for details of the arguments.
1743-
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
1754+
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = nil) #:nodoc:
17441755
return self unless p_addr
17451756

17461757
Class.new(self) {
@@ -1758,6 +1769,7 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
17581769

17591770
@proxy_user = p_user
17601771
@proxy_pass = p_pass
1772+
@proxy_use_ssl = p_use_ssl
17611773
}
17621774
end
17631775

@@ -1782,6 +1794,9 @@ def proxy_class?
17821794
# Returns the password for accessing the proxy, or +nil+ if none;
17831795
# see Net::HTTP@Proxy+Server.
17841796
attr_reader :proxy_pass
1797+
1798+
# Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil.
1799+
attr_reader :proxy_use_ssl
17851800
end
17861801

17871802
# Returns +true+ if a proxy server is defined, +false+ otherwise;

test/net/http/test_https_proxy.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,53 @@ def test_https_proxy_authentication
4343
assert_join_threads([client_thread, server_thread])
4444
}
4545
end
46+
47+
def test_https_proxy_ssl_connection
48+
begin
49+
OpenSSL
50+
rescue LoadError
51+
omit 'autoload problem. see [ruby-dev:45021][Bug #5786]'
52+
end
53+
54+
tcpserver = TCPServer.new("127.0.0.1", 0)
55+
ctx = OpenSSL::SSL::SSLContext.new
56+
ctx.key = OpenSSL::PKey::RSA.new 2048
57+
ctx.cert = OpenSSL::X509::Certificate.new
58+
ctx.cert.subject = OpenSSL::X509::Name.new [['CN', 'localhost']]
59+
ctx.cert.issuer = ctx.cert.subject
60+
ctx.cert.public_key = ctx.key
61+
ctx.cert.not_before = Time.now
62+
ctx.cert.not_after = Time.now + 60 * 60 * 24
63+
ctx.cert.sign ctx.key, OpenSSL::Digest::SHA1.new
64+
serv = OpenSSL::SSL::SSLServer.new(tcpserver, ctx)
65+
66+
_, port, _, _ = serv.addr
67+
client_thread = Thread.new {
68+
proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password', true)
69+
http = proxy.new("foo.example.org", 8000)
70+
http.use_ssl = true
71+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
72+
begin
73+
http.start
74+
rescue EOFError
75+
end
76+
}
77+
server_thread = Thread.new {
78+
sock = serv.accept
79+
begin
80+
proxy_request = sock.gets("\r\n\r\n")
81+
assert_equal(
82+
"CONNECT foo.example.org:8000 HTTP/1.1\r\n" +
83+
"Host: foo.example.org:8000\r\n" +
84+
"Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" +
85+
"\r\n",
86+
proxy_request,
87+
"[ruby-dev:25673]")
88+
ensure
89+
sock.close
90+
end
91+
}
92+
assert_join_threads([client_thread, server_thread])
93+
end
4694
end if defined?(OpenSSL)
4795

0 commit comments

Comments
 (0)