ruby-changes:26495
From: drbrain <ko1@a...>
Date: Sat, 22 Dec 2012 05:36:20 +0900 (JST)
Subject: [ruby-changes:26495] drbrain:r38546 (trunk): * lib/net/http.rb: Requests may be created with a URI which sets the
drbrain 2012-12-22 05:36:07 +0900 (Sat, 22 Dec 2012) New Revision: 38546 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=38546 Log: * lib/net/http.rb: Requests may be created with a URI which sets the Host header. Responses contain the requested URI for easier redirect following. [ruby-trunk - Feature #6482] * lib/net/http/generic_request.rb: ditto. * lib/net/http/response.rb: ditto.j * NEWS (net/http): Updated for above. * test/net/http/test_http.rb: Tests for above. * test/net/http/test_http.rb: ditto. * test/net/http/test_httpresponse.rb: ditto. Modified files: trunk/ChangeLog trunk/NEWS trunk/lib/net/http/generic_request.rb trunk/lib/net/http/response.rb trunk/lib/net/http.rb trunk/test/net/http/test_http.rb trunk/test/net/http/test_httpresponse.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 38545) +++ ChangeLog (revision 38546) @@ -1,3 +1,15 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sat Dec 22 05:34:54 2012 Eric Hodel <drbrain@s...> + + * lib/net/http.rb: Requests may be created with a URI which sets the + Host header. Responses contain the requested URI for easier redirect + following. [ruby-trunk - Feature #6482] + * lib/net/http/generic_request.rb: ditto. + * lib/net/http/response.rb: ditto. + * NEWS (net/http): Updated for above. + * test/net/http/test_http.rb: Tests for above. + * test/net/http/test_http.rb: ditto. + * test/net/http/test_httpresponse.rb: ditto. + Sat Dec 22 02:35:00 2012 Zachary Scott <zachary@z...> * lib/irb/slex.rb(#match): Typo, should be D_DETAIL Index: lib/net/http/response.rb =================================================================== --- lib/net/http/response.rb (revision 38545) +++ lib/net/http/response.rb (revision 38546) @@ -79,6 +79,7 @@ class Net::HTTPResponse https://github.com/ruby/ruby/blob/trunk/lib/net/http/response.rb#L79 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 https://github.com/ruby/ruby/blob/trunk/lib/net/http/response.rb#L94 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 https://github.com/ruby/ruby/blob/trunk/lib/net/http/response.rb#L123 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 38545) +++ lib/net/http/generic_request.rb (revision 38546) @@ -7,10 +7,22 @@ class Net::HTTPGenericRequest https://github.com/ruby/ruby/blob/trunk/lib/net/http/generic_request.rb#L7 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 https://github.com/ruby/ruby/blob/trunk/lib/net/http/generic_request.rb#L41 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 https://github.com/ruby/ruby/blob/trunk/lib/net/http/generic_request.rb#L49 attr_reader :method attr_reader :path + attr_reader :uri def inspect "\#<#{self.class} #{@method}>" @@ -82,6 +96,8 @@ class Net::HTTPGenericRequest https://github.com/ruby/ruby/blob/trunk/lib/net/http/generic_request.rb#L96 # def exec(sock, ver, path) #:nodoc: internal use only + self['host'] = "#{@uri.host}:#{@uri.port}" if @uri + if @body send_request_with_body sock, ver, path, @body elsif @body_stream @@ -93,6 +109,23 @@ class Net::HTTPGenericRequest https://github.com/ruby/ruby/blob/trunk/lib/net/http/generic_request.rb#L109 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 38545) +++ lib/net/http.rb (revision 38546) @@ -93,7 +93,7 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L93 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L111 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L172 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L190 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L207 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L225 # # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L242 # 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: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L261 # # 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 @@ -472,7 +476,7 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L476 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 @@ -496,7 +500,7 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L500 # { "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, @@ -868,7 +872,7 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L872 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) } @@ -884,8 +888,10 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L888 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 @@ -1077,7 +1083,9 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L1083 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 @@ -1403,6 +1411,9 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L1411 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? } @@ -1444,6 +1455,11 @@ module Net #:nodoc: https://github.com/ruby/ruby/blob/trunk/lib/net/http.rb#L1455 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: NEWS =================================================================== --- NEWS (revision 38545) +++ NEWS (revision 38546) @@ -205,6 +205,10 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L205 default. See Net::HTTP for details. * SSL sessions are now reused across connections for a single instance. This speeds up connection by using a previously negotiated session. + * Requests may be created from a URI which sets the request_uri and host + header of the request (but does not change the host connected to). + * Responses contain the URI requested which allows easier implementation of + redirect following. * new methods: * Net::HTTP#local_host * Net::HTTP#local_host= Index: test/net/http/test_httpresponse.rb =================================================================== --- test/net/http/test_httpresponse.rb (revision 38545) +++ test/net/http/test_httpresponse.rb (revision 38546) @@ -203,6 +203,21 @@ EOS https://github.com/ruby/ruby/blob/trunk/test/net/http/test_httpresponse.rb#L203 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 38545) +++ test/net/http/test_http.rb (revision 38546) @@ -409,6 +409,8 @@ module TestNetHTTP_version_1_2_methods https://github.com/ruby/ruby/blob/trunk/test/net/http/test_http.rb#L409 _test_request__HEAD http _test_request__POST http _test_request__stream_body http + _test_request__uri http + _test_request__uri_host http } end @@ -488,6 +490,51 @@ module TestNetHTTP_version_1_2_methods https://github.com/ruby/ruby/blob/trunk/test/net/http/test_http.rb#L490 assert_equal data, res.body end + def _test_request__path(http) + uri = URI 'https://example/' + req = Net::HTTP::Get.new('/') + + res = http.request(req) + + assert_kind_of URI::Generic, req.uri + + refute_equal uri, req.uri + + assert_equal uri, res.uri + + refute_same uri, req.uri + refute_same req.uri, res.uri + 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://example:#{http.port}"), res.uri + end + def test_send_request start {|http| _test_send_request__GET http @@ -837,3 +884,4 @@ class TestNetHTTPLocalBind < Test::Unit: https://github.com/ruby/ruby/blob/trunk/test/net/http/test_http.rb#L884 assert_equal(http.local_port, res.body) end end + -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/