diff --git a/lib/net/http.rb b/lib/net/http.rb index 41b8357a..d3e0e28e 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1103,7 +1103,7 @@ class << HTTP # For proxy-defining arguments +p_addr+ through +p_no_proxy+, # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. # - def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_no_proxy = nil) + 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) http = super address, port 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 http.proxy_port = @proxy_port http.proxy_user = @proxy_user http.proxy_pass = @proxy_pass + http.proxy_use_ssl = @proxy_use_ssl elsif p_addr == :ENV then http.proxy_from_env = true else @@ -1123,6 +1124,7 @@ def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_p http.proxy_port = p_port || default_port http.proxy_user = p_user http.proxy_pass = p_pass + http.proxy_use_ssl = p_use_ssl end http @@ -1158,6 +1160,7 @@ def initialize(address, port = nil) # :nodoc: @proxy_port = nil @proxy_user = nil @proxy_pass = nil + @proxy_use_ssl = nil @use_ssl = false @ssl_context = nil @@ -1292,6 +1295,7 @@ def response_body_encoding=(value) # Sets the proxy password; # see {Proxy Server}[rdoc-ref:Net::HTTP@Proxy+Server]. attr_writer :proxy_pass + attr_writer :proxy_use_ssl # Returns the IP address for the connection. # @@ -1651,7 +1655,13 @@ def connect debug "opened" if use_ssl? if proxy? - plain_sock = BufferedIO.new(s, read_timeout: @read_timeout, + if @proxy_use_ssl + proxy_sock = OpenSSL::SSL::SSLSocket.new(s) + ssl_socket_connect(proxy_sock, @open_timeout) + else + proxy_sock = s + end + proxy_sock = BufferedIO.new(proxy_sock, read_timeout: @read_timeout, write_timeout: @write_timeout, continue_timeout: @continue_timeout, debug_output: @debug_output) @@ -1662,8 +1672,8 @@ def connect buf << "Proxy-Authorization: Basic #{credential}\r\n" end buf << "\r\n" - plain_sock.write(buf) - HTTPResponse.read_new(plain_sock).value + proxy_sock.write(buf) + HTTPResponse.read_new(proxy_sock).value # assuming nothing left in buffers after successful CONNECT response end @@ -1771,13 +1781,14 @@ def do_finish @proxy_port = nil @proxy_user = nil @proxy_pass = nil + @proxy_use_ssl = nil # Creates an \HTTP proxy class which behaves like \Net::HTTP, but # performs all access via the specified proxy. # # This class is obsolete. You may pass these same parameters directly to # \Net::HTTP.new. See Net::HTTP.new for details of the arguments. - def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc: + def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil, p_use_ssl = nil) #:nodoc: return self unless p_addr Class.new(self) { @@ -1795,6 +1806,7 @@ def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) #:nodoc: @proxy_user = p_user @proxy_pass = p_pass + @proxy_use_ssl = p_use_ssl } end @@ -1819,6 +1831,9 @@ def proxy_class? # Returns the password for accessing the proxy, or +nil+ if none; # see Net::HTTP@Proxy+Server. attr_reader :proxy_pass + + # Use SSL when talking to the proxy. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_use_ssl end # Returns +true+ if a proxy server is defined, +false+ otherwise; diff --git a/test/net/http/test_https_proxy.rb b/test/net/http/test_https_proxy.rb index 4c2a92cc..d7da0cef 100644 --- a/test/net/http/test_https_proxy.rb +++ b/test/net/http/test_https_proxy.rb @@ -43,5 +43,53 @@ def test_https_proxy_authentication assert_join_threads([client_thread, server_thread]) } end + + def test_https_proxy_ssl_connection + begin + OpenSSL + rescue LoadError + omit 'autoload problem. see [ruby-dev:45021][Bug #5786]' + end + + tcpserver = TCPServer.new("127.0.0.1", 0) + ctx = OpenSSL::SSL::SSLContext.new + ctx.key = OpenSSL::PKey::RSA.new 2048 + ctx.cert = OpenSSL::X509::Certificate.new + ctx.cert.subject = OpenSSL::X509::Name.new [['CN', 'localhost']] + ctx.cert.issuer = ctx.cert.subject + ctx.cert.public_key = ctx.key + ctx.cert.not_before = Time.now + ctx.cert.not_after = Time.now + 60 * 60 * 24 + ctx.cert.sign ctx.key, OpenSSL::Digest::SHA1.new + serv = OpenSSL::SSL::SSLServer.new(tcpserver, ctx) + + _, port, _, _ = serv.addr + client_thread = Thread.new { + proxy = Net::HTTP.Proxy("127.0.0.1", port, 'user', 'password', true) + http = proxy.new("foo.example.org", 8000) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + begin + http.start + rescue EOFError + end + } + server_thread = Thread.new { + sock = serv.accept + begin + proxy_request = sock.gets("\r\n\r\n") + assert_equal( + "CONNECT foo.example.org:8000 HTTP/1.1\r\n" + + "Host: foo.example.org:8000\r\n" + + "Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n" + + "\r\n", + proxy_request, + "[ruby-core:96672]") + ensure + sock.close + end + } + assert_join_threads([client_thread, server_thread]) + end end if defined?(OpenSSL)