Index: lib/net/http/response.rb =================================================================== --- lib/net/http/response.rb (revision 36482) +++ lib/net/http/response.rb (working copy) @@ -79,6 +79,7 @@ class Net::HTTPResponse initialize_http_header nil @body = nil @read = false + @uri = nil end # The HTTP version supported by the server. @@ -93,6 +94,10 @@ class Net::HTTPResponse attr_reader :message alias msg message # :nodoc: obsolete + # The URI used to fetch this response. The response URI is only available + # if a URI was used to create the request. + attr_reader :uri + def inspect "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" end @@ -118,6 +123,10 @@ class Net::HTTPResponse error! unless self.kind_of?(Net::HTTPSuccess) end + def uri= uri # :nodoc: + @uri = uri.dup if uri + end + # # header (for backward compatibility only; DO NOT USE) # Index: lib/net/http/generic_request.rb =================================================================== --- lib/net/http/generic_request.rb (revision 36482) +++ lib/net/http/generic_request.rb (working copy) @@ -7,10 +7,22 @@ class Net::HTTPGenericRequest include Net::HTTPHeader - def initialize(m, reqbody, resbody, path, initheader = nil) + def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) @method = m @request_has_body = reqbody @response_has_body = resbody + + if URI === uri_or_path then + @uri = uri_or_path.dup + host = @uri.hostname + host += ":#{@uri.port}" if @uri.port != @uri.class::DEFAULT_PORT + path = uri_or_path.request_uri + else + @uri = nil + host = nil + path = uri_or_path + end + raise ArgumentError, "no HTTP request path given" unless path raise ArgumentError, "HTTP request path is empty" if path.empty? @path = path @@ -29,6 +41,7 @@ class Net::HTTPGenericRequest initialize_http_header initheader self['Accept'] ||= '*/*' self['User-Agent'] ||= 'Ruby' + self['Host'] ||= host @body = nil @body_stream = nil @body_data = nil @@ -36,6 +49,7 @@ class Net::HTTPGenericRequest attr_reader :method attr_reader :path + attr_reader :uri def inspect "\#<#{self.class} #{@method}>" @@ -93,6 +107,23 @@ class Net::HTTPGenericRequest end end + def update_uri(host, port, ssl) # :nodoc: internal use only + return unless @uri + + @uri.host = host + @uri.port = port + + scheme = ssl ? 'https' : 'http' + + # convert the class of the URI + unless scheme == @uri.scheme then + new_uri = @uri.to_s.sub(/^https?/, scheme) + @uri = URI new_uri + end + + @uri + end + private class Chunker #:nodoc: Index: lib/net/http.rb =================================================================== --- lib/net/http.rb (revision 36482) +++ lib/net/http.rb (working copy) @@ -93,7 +93,7 @@ module Net #:nodoc: # uri = URI('http://example.com/some_path?query=string') # # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri.request_uri + # request = Net::HTTP::Get.new uri # # response = http.request request # Net::HTTPResponse object # end @@ -111,6 +111,10 @@ module Net #:nodoc: # will automatically open a connection to the server if one is not currently # open. You can manually close the connection with #finish. # + # For all the Net::HTTP request objects and shortcut request methods you may + # supply either a String for the request path or a URI from which Net::HTTP + # will extract the request path. + # # === Response Data # # uri = URI('http://example.com/index.html') @@ -168,7 +172,7 @@ module Net #:nodoc: # creates a urlencoded POST body: # # uri = URI('http://www.example.com/todo.cgi') - # req = Net::HTTP::Post.new(uri.path) + # req = Net::HTTP::Post.new(uri) # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31') # # res = Net::HTTP.start(uri.hostname, uri.port) do |http| @@ -186,7 +190,7 @@ module Net #:nodoc: # multipart/form-data use Net::HTTPRequest#body= and # Net::HTTPRequest#content_type=: # - # req = Net::HTTP::Post.new(uri.path) + # req = Net::HTTP::Post.new(uri) # req.body = multipart_data # req.content_type = 'multipart/form-data' # @@ -203,7 +207,7 @@ module Net #:nodoc: # uri = URI('http://example.com/cached_response') # file = File.stat 'cached_response' # - # req = Net::HTTP::Get.new(uri.request_uri) + # req = Net::HTTP::Get.new(uri) # req['If-Modified-Since'] = file.mtime.rfc2822 # # res = Net::HTTP.start(uri.hostname, uri.port) {|http| @@ -221,7 +225,7 @@ module Net #:nodoc: # # uri = URI('http://example.com/index.html?key=value') # - # req = Net::HTTP::Get.new(uri.request_uri) + # req = Net::HTTP::Get.new(uri) # req.basic_auth 'user', 'pass' # # res = Net::HTTP.start(uri.hostname, uri.port) {|http| @@ -238,7 +242,7 @@ module Net #:nodoc: # uri = URI('http://example.com/large_file') # # Net::HTTP.start(uri.host, uri.port) do |http| - # request = Net::HTTP::Get.new uri.request_uri + # request = Net::HTTP::Get.new uri # # http.request request do |response| # open 'large_file', 'w' do |io| @@ -257,7 +261,7 @@ module Net #:nodoc: # # Net::HTTP.start(uri.host, uri.port, # :use_ssl => uri.scheme == 'https') do |http| - # request = Net::HTTP::Get.new uri.request_uri + # request = Net::HTTP::Get.new uri # # response = http.request request # Net::HTTPResponse object # end @@ -471,7 +475,7 @@ module Net #:nodoc: uri = uri_or_host start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') {|http| - return http.request_get(uri.request_uri, &block) + return http.request_get(uri, &block) } end end @@ -495,7 +499,7 @@ module Net #:nodoc: # { "q" => "ruby", "max" => "50" } # def HTTP.post_form(url, params) - req = Post.new(url.request_uri) + req = Post.new(url) req.form_data = params req.basic_auth url.user, url.password if url.user start(url.hostname, url.port, @@ -862,7 +866,7 @@ module Net #:nodoc: conn_port = port end - D "opening connection to #{conn_address}..." + D "opening connection to #{conn_address}:#{conn_port}..." s = Timeout.timeout(@open_timeout, Net::OpenTimeout) { TCPSocket.open(conn_address, conn_port, @local_host, @local_port) } @@ -878,8 +882,10 @@ module Net #:nodoc: end @ssl_context = OpenSSL::SSL::SSLContext.new @ssl_context.set_params(ssl_parameters) + D "starting SSL for #{conn_address}:#{conn_port}..." s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) s.sync_close = true + D "SSL established" end @socket = BufferedIO.new(s) @socket.read_timeout = @read_timeout @@ -1068,7 +1074,9 @@ module Net #:nodoc: public - # Gets data from +path+ on the connected-to host. + # Retrieves data from +path+ on the connected-to host which may be an + # absolute path String or a URI to extract the path from. + # # +initheader+ must be a Hash like { 'Accept' => '*/*', ... }, # and it defaults to an empty hash. # If +initheader+ doesn't have the key 'accept-encoding', then @@ -1394,6 +1402,9 @@ module Net #:nodoc: begin res = HTTPResponse.read_new(@socket) end while res.kind_of?(HTTPContinue) + + res.uri = req.uri + res.reading_body(@socket, req.response_body_permitted?) { yield res if block_given? } @@ -1435,6 +1446,11 @@ module Net #:nodoc: if not req.response_body_permitted? and @close_on_empty_response req['connection'] ||= 'close' end + + host = req['host'] || address + host = $1 if host =~ /(.*):\d+$/ + req.update_uri host, port, use_ssl? + req['host'] ||= addr_port() end Index: test/net/http/test_httpresponse.rb =================================================================== --- test/net/http/test_httpresponse.rb (revision 36482) +++ test/net/http/test_httpresponse.rb (working copy) @@ -184,6 +184,21 @@ EOS assert_equal 'hello', body end + def test_uri_equals + uri = URI 'http://example' + + response = Net::HTTPResponse.new '1.1', 200, 'OK' + + response.uri = nil + + assert_nil response.uri + + response.uri = uri + + assert_equal uri, response.uri + refute_same uri, response.uri + end + private def dummy_io(str) Index: test/net/http/test_http.rb =================================================================== --- test/net/http/test_http.rb (revision 36482) +++ test/net/http/test_http.rb (working copy) @@ -385,6 +385,8 @@ module TestNetHTTP_version_1_2_methods _test_request__HEAD http _test_request__POST http _test_request__stream_body http + _test_request__uri http + _test_request__uri_host http } end @@ -464,6 +466,35 @@ module TestNetHTTP_version_1_2_methods assert_equal data, res.body end + def _test_request__uri(http) + uri = URI 'https://example/' + req = Net::HTTP::Get.new(uri) + + res = http.request(req) + + assert_kind_of URI::Generic, req.uri + + refute_equal uri, req.uri + + assert_equal req.uri, res.uri + + refute_same uri, req.uri + refute_same req.uri, res.uri + end + + def _test_request__uri_host(http) + uri = URI 'http://example/' + + req = Net::HTTP::Get.new(uri) + req['host'] = 'other.example' + + res = http.request(req) + + assert_kind_of URI::Generic, req.uri + + assert_equal URI("http://other.example:#{http.port}"), res.uri + end + def test_send_request start {|http| _test_send_request__GET http @@ -813,3 +844,4 @@ class TestNetHTTPLocalBind < Test::Unit: assert_equal(http.local_port, res.body) end end +