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

ruby-changes:19815

From: nahi <ko1@a...>
Date: Tue, 31 May 2011 17:10:50 +0900 (JST)
Subject: [ruby-changes:19815] nahi:r31860 (trunk): * lib/net/http.rb, lib/net/protocol.rb: Allow to configure to wait

nahi	2011-05-31 17:10:42 +0900 (Tue, 31 May 2011)

  New Revision: 31860

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=31860

  Log:
    * lib/net/http.rb, lib/net/protocol.rb: Allow to configure to wait
      server returning '100 continue' response befor sending HTTP request
      body. See NEWS for more detail. See #3622.
      Original patch is made by Eric Hodel <drbrain@s...>.
    
    * test/net/http/test_http.rb: test it.
    
    * NEWS: Add new feature.
    
    On my env (Ubuntu 11.04 64bit),
    9510 tests, 2203824 assertions, 0 failures, 0 errors, 29 skips
    ->
    9514 tests, 2203836 assertions, 0 failures, 0 errors, 29 skips

  Modified files:
    trunk/ChangeLog
    trunk/NEWS
    trunk/lib/net/http.rb
    trunk/lib/net/protocol.rb
    trunk/test/net/http/test_http.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 31859)
+++ ChangeLog	(revision 31860)
@@ -1,3 +1,14 @@
+Tue May 31 17:03:24 2011  Hiroshi Nakamura  <nahi@r...>
+
+	* lib/net/http.rb, lib/net/protocol.rb: Allow to configure to wait
+	  server returning '100 continue' response befor sending HTTP request
+	  body. See NEWS for more detail. See #3622.
+	  Original patch is made by Eric Hodel <drbrain@s...>.
+
+	* test/net/http/test_http.rb: test it.
+
+	* NEWS: Add new feature.
+
 Tue May 31 14:17:49 2011  NAKAMURA Usaku  <usa@r...>
 
 	* io.c (rb_io_s_pipe): potential bug. the mode of read IO is set as
Index: lib/net/http.rb
===================================================================
--- lib/net/http.rb	(revision 31859)
+++ lib/net/http.rb	(revision 31860)
@@ -581,6 +581,7 @@
       @started = false
       @open_timeout = nil
       @read_timeout = 60
+      @continue_timeout = nil
       @debug_output = nil
       @use_ssl = false
       @ssl_context = nil
@@ -634,6 +635,16 @@
       @read_timeout = sec
     end
 
+    # Seconds to wait for 100 Continue response.  If the HTTP object does not
+    # receive a response in this many seconds it sends the request body.
+    attr_reader :continue_timeout
+
+    # Setter for the continue_timeout attribute.
+    def continue_timeout=(sec)
+      @socket.continue_timeout = sec if @socket
+      @continue_timeout = sec
+    end
+
     # Returns true if the HTTP session has been started.
     def started?
       @started
@@ -764,6 +775,7 @@
       end
       @socket = BufferedIO.new(s)
       @socket.read_timeout = @read_timeout
+      @socket.continue_timeout = @continue_timeout
       @socket.debug_output = @debug_output
       if use_ssl?
         begin
@@ -1298,12 +1310,15 @@
 
     def transport_request(req)
       begin_transport req
-      req.exec @socket, @curr_http_version, edit_path(req.path)
-      begin
-        res = HTTPResponse.read_new(@socket)
-      end while res.kind_of?(HTTPContinue)
-      res.reading_body(@socket, req.response_body_permitted?) {
-        yield res if block_given?
+      res = catch(:response) {
+        req.exec @socket, @curr_http_version, edit_path(req.path)
+        begin
+          res = HTTPResponse.read_new(@socket)
+        end while res.kind_of?(HTTPContinue)
+        res.reading_body(@socket, req.response_body_permitted?) {
+          yield res if block_given?
+        }
+        res
       }
       end_transport req, res
       res
@@ -1915,6 +1930,7 @@
       delete 'Transfer-Encoding'
       supply_default_content_type
       write_header sock, ver, path
+      wait_for_continue sock, ver if sock.continue_timeout
       sock.write body
     end
 
@@ -1925,6 +1941,7 @@
       end
       supply_default_content_type
       write_header sock, ver, path
+      wait_for_continue sock, ver if sock.continue_timeout
       if chunked?
         while s = f.read(1024)
           sock.write(sprintf("%x\r\n", s.length) << s << "\r\n")
@@ -2030,6 +2047,22 @@
       set_content_type 'application/x-www-form-urlencoded'
     end
 
+    ##
+    # Waits up to the continue timeout for a response from the server provided
+    # we're speaking HTTP 1.1 and are expecting a 100-continue response.
+
+    def wait_for_continue(sock, ver)
+      if ver >= '1.1' and @header['expect'] and
+          @header['expect'].include?('100-continue')
+        if IO.select([sock.io], nil, nil, sock.continue_timeout)
+          res = HTTPResponse.read_new(sock)
+          unless res.kind_of?(Net::HTTPContinue)
+            throw :response, res
+          end
+        end
+      end
+    end
+
     def write_header(sock, ver, path)
       buf = "#{@method} #{path} HTTP/#{ver}\r\n"
       each_capitalized do |k,v|
Index: lib/net/protocol.rb
===================================================================
--- lib/net/protocol.rb	(revision 31859)
+++ lib/net/protocol.rb	(revision 31860)
@@ -50,12 +50,14 @@
     def initialize(io)
       @io = io
       @read_timeout = 60
+      @continue_timeout = nil
       @debug_output = nil
       @rbuf = ''
     end
 
     attr_reader :io
     attr_accessor :read_timeout
+    attr_accessor :continue_timeout
     attr_accessor :debug_output
 
     def inspect
Index: NEWS
===================================================================
--- NEWS	(revision 31859)
+++ NEWS	(revision 31860)
@@ -98,6 +98,17 @@
 * net/http
   * SNI (Server Name Indication) supported for HTTPS. 
 
+  * Allow to configure to wait server returning '100 continue' response
+    before sending HTTP request body. Set Net::HTTP#continue_timeout AND pass
+    'expect' => '100-continue' to a extra HTTP header. 
+
+    For example, the following code sends HTTP header and waits for getting
+    '100 continue' response before sending HTTP request body. When 0.5 [sec]
+    timeout occurs or the server send '100 continue', the client sends HTTP
+    request body.
+      http.continue_timeout = 0.5
+      http.request_post('/continue', 'body=BODY', 'expect' => '100-continue')
+
 * openssl
   * PKey::RSA and PKey::DSA now use the generic X.509 encoding scheme 
     (e.g. used in a X.509 certificate's Subject Public Key Info) when
Index: test/net/http/test_http.rb
===================================================================
--- test/net/http/test_http.rb	(revision 31859)
+++ test/net/http/test_http.rb	(revision 31860)
@@ -464,3 +464,84 @@
     }
   end
 end
+
+class TestNetHTTPContinue < Test::Unit::TestCase
+  CONFIG = {
+    'host' => '127.0.0.1',
+    'port' => 10081,
+    'proxy_host' => nil,
+    'proxy_port' => nil,
+    'chunked' => true,
+  }
+
+  include TestNetHTTPUtils
+
+  def logfile
+    @debug = StringIO.new('')
+  end
+
+  def mount_proc(&block)
+    @server.mount('/continue', WEBrick::HTTPServlet::ProcHandler.new(block.to_proc))
+  end
+
+  def test_expect_continue
+    mount_proc {|req, res|
+      req.continue
+      res.body = req.query['body']
+    }
+    start {|http|
+      http.continue_timeout = 0.2
+      http.request_post('/continue', 'body=BODY', 'expect' => '100-continue') {|res|
+        assert_equal('BODY', res.read_body)
+      }
+    }
+    assert_match(/Expect: 100-continue/, @debug.string)
+    assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+  end
+
+  def test_expect_continue_timeout
+    mount_proc {|req, res|
+      sleep 0.2
+      req.continue # just ignored because it's '100'
+      res.body = req.query['body']
+    }
+    start {|http|
+      http.continue_timeout = 0
+      http.request_post('/continue', 'body=BODY', 'expect' => '100-continue') {|res|
+        assert_equal('BODY', res.read_body)
+      }
+    }
+    assert_match(/Expect: 100-continue/, @debug.string)
+    assert_match(/HTTP\/1.1 100 continue/, @debug.string)
+  end
+
+  def test_expect_continue_error
+    mount_proc {|req, res|
+      res.status = 501
+      res.body = req.query['body']
+    }
+    start {|http|
+      http.continue_timeout = 0
+      http.request_post('/continue', 'body=ERROR', 'expect' => '100-continue') {|res|
+        assert_equal('ERROR', res.read_body)
+      }
+    }
+    assert_match(/Expect: 100-continue/, @debug.string)
+    assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+  end
+
+  def test_expect_continue_error_while_waiting
+    mount_proc {|req, res|
+      res.status = 501
+      res.body = req.query['body']
+    }
+    start {|http|
+      http.continue_timeout = 0.5
+      http.request_post('/continue', 'body=ERROR', 'expect' => '100-continue') {|res|
+        assert_equal('ERROR', res.read_body)
+      }
+    }
+    assert_match(/Expect: 100-continue/, @debug.string)
+    assert_not_match(/HTTP\/1.1 100 continue/, @debug.string)
+  end
+end

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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