Feature #3622 » expect-continue.diff
| lib/net/http.rb (working copy) | ||
|---|---|---|
| 
           @started = false 
   | 
||
| 
           @open_timeout = nil 
   | 
||
| 
           @read_timeout = 60 
   | 
||
| 
           @continue_timeout = nil 
   | 
||
| 
           @debug_output = nil 
   | 
||
| 
           @use_ssl = false 
   | 
||
| 
           @ssl_context = nil 
   | 
||
| ... | ... | |
| 
           @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 is started. 
   | 
||
| 
         def started? 
   | 
||
| 
           @started 
   | 
||
| ... | ... | |
| 
         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 
   | 
||
| ... | ... | |
| 
           self.content_length = body.bytesize 
   | 
||
| 
           delete 'Transfer-Encoding' 
   | 
||
| 
           supply_default_content_type 
   | 
||
| 
           wait_for_continue sock, ver if sock.continue_timeout 
   | 
||
| 
           write_header sock, ver, path 
   | 
||
| 
           sock.write body 
   | 
||
| 
         end 
   | 
||
| ... | ... | |
| 
                 "Content-Length not given and Transfer-Encoding is not `chunked'" 
   | 
||
| 
           end 
   | 
||
| 
           supply_default_content_type 
   | 
||
| 
           wait_for_continue sock, ver if sock.continue_timeout 
   | 
||
| 
           write_header sock, ver, path 
   | 
||
| 
           if chunked? 
   | 
||
| 
             while s = f.read(1024) 
   | 
||
| ... | ... | |
| 
           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') then 
   | 
||
| 
             if IO.select [sock.io], nil, nil, sock.continue_timeout then 
   | 
||
| 
               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| 
   | 
||
| lib/net/protocol.rb (working copy) | ||
|---|---|---|
| 
         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 
   | 
||
| test/net/http/test_http.rb (working copy) | ||
|---|---|---|
| 
       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 
   | 
||
| 
     =begin 
   | 
||
| 
     class TestNetHTTP_proxy < Test::Unit::TestCase 
   | 
||
| 
       CONFIG = { 
   | 
||