[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]