Skip to content

Commit ae2d83f

Browse files
evgenihsbt
authored andcommitted
implement talking SSL to the proxy too
https://bugs.ruby-lang.org/issues/16482
1 parent d6edfa2 commit ae2d83f

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
@@ -1103,7 +1103,7 @@ class << HTTP
11031103
# For proxy-defining arguments +p_addr+ through +p_no_proxy+,
11041104
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
11051105
#
1106-
def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil)
1106+
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)
11071107
http = super address, port
11081108

11091109
if proxy_class? then # from Net::HTTP::Proxy()
@@ -1112,6 +1112,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p
11121112
http.proxy_port = @proxy_port
11131113
http.proxy_user = @proxy_user
11141114
http.proxy_pass = @proxy_pass
1115+
http.proxy_use_ssl = @proxy_use_ssl
11151116
elsif p_addr == :ENV then
11161117
http.proxy_from_env = true
11171118
else
@@ -1123,6 +1124,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p
11231124
http.proxy_port = p_port || default_port
11241125
http.proxy_user = p_user
11251126
http.proxy_pass = p_pass
1127+
http.proxy_use_ssl = p_use_ssl
11261128
end
11271129

11281130
http
@@ -1158,6 +1160,7 @@ def initialize(address, port = nil) # :nodoc:
11581160
@proxy_port = nil
11591161
@proxy_user = nil
11601162
@proxy_pass = nil
1163+
@proxy_use_ssl = nil
11611164

11621165
@use_ssl = false
11631166
@ssl_context = nil
@@ -1292,6 +1295,7 @@ def response_body_encoding=(value)
12921295
# Sets the proxy password;
12931296
# see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server].
12941297
attr_writer :proxy_pass
1298+
attr_writer :proxy_use_ssl
12951299

12961300
# Returns the IP address for the connection.
12971301
#
@@ -1651,7 +1655,13 @@ def connect
16511655
debug "opened"
16521656
if use_ssl?
16531657
if proxy?
1654-
plain_sock = BufferedIO.new(s, read_timeout: @read_timeout,
1658+
if @proxy_use_ssl
1659+
proxy_sock = OpenSSL::SSL::SSLSocket.new(s)
1660+
ssl_socket_connect(proxy_sock, @open_timeout)
1661+
else
1662+
proxy_sock = s
1663+
end
1664+
proxy_sock = BufferedIO.new(proxy_sock, read_timeout: @read_timeout,
16551665
write_timeout: @write_timeout,
16561666
continue_timeout: @continue_timeout,
16571667
debug_output: @debug_output)
@@ -1662,8 +1672,8 @@ def connect
16621672
buf << "Proxy-Authorization: Basic #{credential}\r\n"
16631673
end
16641674
buf << "\r\n"
1665-
plain_sock.write(buf)
1666-
HTTPResponse.read_new(plain_sock).value
1675+
proxy_sock.write(buf)
1676+
HTTPResponse.read_new(proxy_sock).value
16671677
# assuming nothing left in buffers after successful CONNECT response
16681678
end
16691679

@@ -1771,13 +1781,14 @@ def do_finish
17711781
@proxy_port = nil
17721782
@proxy_user = nil
17731783
@proxy_pass = nil
1784+
@proxy_use_ssl = nil
17741785

17751786
# Creates an \HTTP proxy class which behaves like \Net::HTTP, but
17761787
# performs all access via the specified proxy.
17771788
#
17781789
# This class is obsolete. You may pass these same parameters directly to
17791790
# \Net::HTTP.new. See Net::HTTP.new for details of the arguments.
1780-
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
1791+
def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = nil) #:nodoc:
17811792
return self unless p_addr
17821793

17831794
Class.new(self) {
@@ -1795,6 +1806,7 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc:
17951806

17961807
@proxy_user = p_user
17971808
@proxy_pass = p_pass
1809+
@proxy_use_ssl = p_use_ssl
17981810
}
17991811
end
18001812

@@ -1819,6 +1831,9 @@ def proxy_class?
18191831
# Returns the password for accessing the proxy, or +nil+ if none;
18201832
# see Net::HTTP@Proxy+Server.
18211833
attr_reader :proxy_pass
1834+
1835+
# Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil.
1836+
attr_reader :proxy_use_ssl
18221837
end
18231838

18241839
# 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-core:96672]")
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)