[macruby-changes] [4083] MacRuby/trunk/lib/net/http.rb

source_changes at macosforge.org source_changes at macosforge.org
Wed May 12 18:29:29 PDT 2010


Revision: 4083
          http://trac.macosforge.org/projects/ruby/changeset/4083
Author:   lsansonetti at apple.com
Date:     2010-05-12 18:29:27 -0700 (Wed, 12 May 2010)
Log Message:
-----------
backporting latest net/http from ruby trunk

Modified Paths:
--------------
    MacRuby/trunk/lib/net/http.rb

Modified: MacRuby/trunk/lib/net/http.rb
===================================================================
--- MacRuby/trunk/lib/net/http.rb	2010-05-13 00:46:18 UTC (rev 4082)
+++ MacRuby/trunk/lib/net/http.rb	2010-05-13 01:29:27 UTC (rev 4083)
@@ -4,28 +4,29 @@
 # Copyright (c) 1999-2007 Yukihiro Matsumoto
 # Copyright (c) 1999-2007 Minero Aoki
 # Copyright (c) 2001 GOTOU Yuuzou
-# 
+#
 # Written and maintained by Minero Aoki <aamine at loveruby.net>.
 # HTTPS support added by GOTOU Yuuzou <gotoyuzo at notwork.org>.
 #
 # This file is derived from "http-access.rb".
 #
 # Documented by Minero Aoki; converted to RDoc by William Webber.
-# 
+#
 # This program is free software. You can re-distribute and/or
 # modify this program under the same terms of ruby itself ---
 # Ruby Distribution License or GNU General Public License.
 #
-# See Net::HTTP for an overview and examples. 
-# 
+# See Net::HTTP for an overview and examples.
+#
 # NOTE: You can find Japanese version of this document here:
 # http://www.ruby-lang.org/ja/man/html/net_http.html
-# 
+#
 #--
-# $Id: http.rb 18805 2008-08-24 03:21:36Z naruse $
-#++ 
+# $Id$
+#++
 
 require 'net/protocol'
+autoload :OpenSSL, 'openssl'
 require 'uri'
 
 module Net   #:nodoc:
@@ -36,29 +37,29 @@
   # :startdoc:
 
   # == What Is This Library?
-  # 
+  #
   # This library provides your program functions to access WWW
   # documents via HTTP, Hyper Text Transfer Protocol version 1.1.
-  # For details of HTTP, refer [RFC2616]
+  # For details of HTTP, refer to [RFC2616]
   # (http://www.ietf.org/rfc/rfc2616.txt).
-  # 
+  #
   # == Examples
-  # 
+  #
   # === Getting Document From WWW Server
-  # 
+  #
   # Example #1: Simple GET+print
-  # 
+  #
   #     require 'net/http'
   #     Net::HTTP.get_print 'www.example.com', '/index.html'
-  # 
+  #
   # Example #2: Simple GET+print by URL
-  # 
+  #
   #     require 'net/http'
   #     require 'uri'
   #     Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
-  # 
+  #
   # Example #3: More generic GET+print
-  # 
+  #
   #     require 'net/http'
   #     require 'uri'
   #
@@ -69,7 +70,7 @@
   #     puts res.body
   #
   # Example #4: More generic GET+print
-  # 
+  #
   #     require 'net/http'
   #
   #     url = URI.parse('http://www.example.com/index.html')
@@ -78,9 +79,9 @@
   #       http.request(req)
   #     }
   #     puts res.body
-  # 
+  #
   # === Posting Form Data
-  # 
+  #
   #     require 'net/http'
   #     require 'uri'
   #
@@ -112,15 +113,15 @@
   #     res = Net::HTTP.post_form(URI.parse('http://www.example.com/search.cgi'),
   #                               {'q' => ['ruby', 'perl'], 'max' => '50'})
   #     puts res.body
-  # 
+  #
   # === Accessing via Proxy
-  # 
+  #
   # Net::HTTP.Proxy creates http proxy class. It has same
   # methods of Net::HTTP but its instances always connect to
   # proxy, instead of given host.
-  # 
+  #
   #     require 'net/http'
-  # 
+  #
   #     proxy_addr = 'your.proxy.host'
   #     proxy_port = 8080
   #             :
@@ -128,20 +129,20 @@
   #       # always connect to your.proxy.addr:8080
   #             :
   #     }
-  # 
+  #
   # Since Net::HTTP.Proxy returns Net::HTTP itself when proxy_addr is nil,
   # there's no need to change code if there's proxy or not.
-  # 
+  #
   # There are two additional parameters in Net::HTTP.Proxy which allow to
   # specify proxy user name and password:
-  # 
+  #
   #     Net::HTTP::Proxy(proxy_addr, proxy_port, proxy_user = nil, proxy_pass = nil)
-  # 
+  #
   # You may use them to work with authorization-enabled proxies:
-  # 
+  #
   #     require 'net/http'
   #     require 'uri'
-  #     
+  #
   #     proxy_host = 'your.proxy.host'
   #     proxy_port = 8080
   #     uri = URI.parse(ENV['http_proxy'])
@@ -154,16 +155,16 @@
   #
   # Note that net/http never rely on HTTP_PROXY environment variable.
   # If you want to use proxy, set it explicitly.
-  # 
+  #
   # === Following Redirection
-  # 
+  #
   #     require 'net/http'
   #     require 'uri'
-  # 
+  #
   #     def fetch(uri_str, limit = 10)
-  #       # You should choose better exception. 
+  #       # You should choose better exception.
   #       raise ArgumentError, 'HTTP redirect too deep' if limit == 0
-  # 
+  #
   #       response = Net::HTTP.get_response(URI.parse(uri_str))
   #       case response
   #       when Net::HTTPSuccess     then response
@@ -172,25 +173,25 @@
   #         response.error!
   #       end
   #     end
-  # 
+  #
   #     print fetch('http://www.ruby-lang.org')
-  # 
+  #
   # Net::HTTPSuccess and Net::HTTPRedirection is a HTTPResponse class.
   # All HTTPResponse objects belong to its own response class which
   # indicate HTTP result status. For details of response classes,
   # see section "HTTP Response Classes".
-  # 
+  #
   # === Basic Authentication
-  # 
+  #
   #     require 'net/http'
-  # 
+  #
   #     Net::HTTP.start('www.example.com') {|http|
   #       req = Net::HTTP::Get.new('/secret-page.html')
   #       req.basic_auth 'account', 'password'
   #       response = http.request(req)
   #       print response.body
   #     }
-  # 
+  #
   # === HTTP Request Classes
   #
   # Here is HTTP request class hierarchy.
@@ -220,7 +221,7 @@
   #       HTTPUnknownResponse
   #       HTTPInformation                    # 1xx
   #           HTTPContinue                       # 100
-  #           HTTPSwitchProtocl                  # 101
+  #           HTTPSwitchProtocol                 # 101
   #       HTTPSuccess                        # 2xx
   #           HTTPOK                             # 200
   #           HTTPCreated                        # 201
@@ -263,28 +264,28 @@
   #           HTTPServiceUnavailable             # 503
   #           HTTPGatewayTimeOut                 # 504
   #           HTTPVersionNotSupported            # 505
-  # 
+  #
   # == Switching Net::HTTP versions
-  # 
+  #
   # You can use net/http.rb 1.1 features (bundled with Ruby 1.6)
   # by calling HTTP.version_1_1. Calling Net::HTTP.version_1_2
   # allows you to use 1.2 features again.
-  # 
+  #
   #     # example
   #     Net::HTTP.start {|http1| ...(http1 has 1.2 features)... }
-  # 
+  #
   #     Net::HTTP.version_1_1
   #     Net::HTTP.start {|http2| ...(http2 has 1.1 features)... }
-  # 
+  #
   #     Net::HTTP.version_1_2
   #     Net::HTTP.start {|http3| ...(http3 has 1.2 features)... }
-  # 
+  #
   # This function is NOT thread-safe.
   #
   class HTTP < Protocol
 
     # :stopdoc:
-    Revision = %q$Revision: 18805 $.split[1]
+    Revision = %q$Revision$.split[1]
     HTTPVersion = '1.1'
     @newimpl = true
     begin
@@ -338,7 +339,7 @@
     #
     # Get body from target and output it to +$stdout+.  The
     # target can either be specified as (+uri+), or as
-    # (+host+, +path+, +port+ = 80); so: 
+    # (+host+, +path+, +port+ = 80); so:
     #
     #    Net::HTTP.get_print URI.parse('http://www.example.com/index.html')
     #
@@ -358,7 +359,7 @@
     # Send a GET request to the target and return the response
     # as a string.  The target can either be specified as
     # (+uri+), or as (+host+, +path+, +port+ = 80); so:
-    # 
+    #
     #    print Net::HTTP.get(URI.parse('http://www.example.com/index.html'))
     #
     # or:
@@ -372,7 +373,7 @@
     # Send a GET request to the target and return the response
     # as a Net::HTTPResponse object.  The target can either be specified as
     # (+uri+), or as (+host+, +path+, +port+ = 80); so:
-    # 
+    #
     #    res = Net::HTTP.get_response(URI.parse('http://www.example.com/index.html'))
     #    print res.body
     #
@@ -442,15 +443,51 @@
       BufferedIO
     end
 
-    # creates a new Net::HTTP object and opens its TCP connection and 
-    # HTTP session.  If the optional block is given, the newly 
-    # created Net::HTTP object is passed to it and closed when the 
+    # call-seq:
+    #   HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block)
+    #   HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &block)
+    #
+    # creates a new Net::HTTP object and opens its TCP connection and
+    # HTTP session.
+    #
+    # Argments are following:
+    # _address_ :: hostname or IP address of the server
+    # _port_    :: port of the server
+    # _p_addr_  :: address of proxy
+    # _p_port_  :: port of proxy
+    # _p_user_  :: user of proxy
+    # _p_pass_  :: pass of proxy
+    # _opt_     :: optional hash
+    #
+    # _opt_ sets following values by its accessor.
+    # The keys are ca_file, ca_path, cert, cert_store, ciphers,
+    # close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout,
+    # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode.
+    # If you set :use_ssl as true, you can use https and default value of
+    # verify_mode is set as OpenSSL::SSL::VERIFY_PEER.
+    #
+    # If the optional block is given, the newly
+    # created Net::HTTP object is passed to it and closed when the
     # block finishes.  In this case, the return value of this method
     # is the return value of the block.  If no block is given, the
     # return value of this method is the newly created Net::HTTP object
     # itself, and the caller is responsible for closing it upon completion.
-    def HTTP.start(address, port = nil, p_addr = nil, p_port = nil, p_user = nil, p_pass = nil, &block) # :yield: +http+
-      new(address, port, p_addr, p_port, p_user, p_pass).start(&block)
+    def HTTP.start(address, *arg, &block) # :yield: +http+
+      arg.pop if opt = Hash.try_convert(arg[-1])
+      port, p_addr, p_port, p_user, p_pass = *arg
+      port = https_default_port if !port && opt && opt[:use_ssl]
+      http = new(address, port, p_addr, p_port, p_user, p_pass)
+
+      if opt
+        opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt) if opt[:use_ssl]
+        http.methods.grep(/\A(\w+)=\z/) do |meth|
+          key = $1.to_sym
+          opt.key?(key) or next
+          http.__send__(meth, opt[key])
+        end
+      end
+
+      http.start(&block)
     end
 
     class << HTTP
@@ -544,11 +581,37 @@
 
     # returns true if use SSL/TLS with HTTP.
     def use_ssl?
-      false   # redefined in net/https
+      @use_ssl
     end
 
+    # Turn on/off SSL.
+    # This flag must be set before starting session.
+    # If you change use_ssl value after session started,
+    # a Net::HTTP object raises IOError.
+    def use_ssl=(flag)
+      flag = (flag ? true : false)
+      if started? and @use_ssl != flag
+        raise IOError, "use_ssl value changed, but session already started"
+      end
+      @use_ssl = flag
+    end
+
+    SSL_ATTRIBUTES = %w(
+      ssl_version key cert ca_file ca_path cert_store ciphers
+      verify_mode verify_callback verify_depth ssl_timeout
+    )
+    attr_accessor(*SSL_ATTRIBUTES)
+
+    # return the X.509 certificates the server presented.
+    def peer_cert
+      if not use_ssl? or not @socket
+        return nil
+      end
+      @socket.io.peer_cert
+    end
+
     # Opens TCP connection and HTTP session.
-    # 
+    #
     # When this method is called with block, gives a HTTP object
     # to the block and closes the TCP connection / HTTP session
     # after the block executed.
@@ -582,8 +645,11 @@
       D "opened"
       if use_ssl?
         ssl_parameters = Hash.new
+        iv_list = instance_variables
         SSL_ATTRIBUTES.each do |name|
-          if value = instance_variable_get("@#{name}")
+          ivname = "@#{name}".intern
+          if iv_list.include?(ivname) and
+             value = instance_variable_get(ivname)
             ssl_parameters[name] = value
           end
         end
@@ -652,9 +718,9 @@
     # Arguments are address/port of proxy host and username/password
     # if authorization on proxy server is required.
     # You can replace the HTTP class with created proxy class.
-    # 
+    #
     # If ADDRESS is nil, this method returns self (Net::HTTP).
-    # 
+    #
     #     # Example
     #     proxy_class = Net::HTTP::Proxy('proxy.example.com', 8080)
     #                     :
@@ -662,7 +728,7 @@
     #       # connecting proxy.foo.org:8080
     #                     :
     #     }
-    # 
+    #
     def HTTP.Proxy(p_addr, p_port = nil, p_user = nil, p_pass = nil)
       return self unless p_addr
       delta = ProxyDelta
@@ -762,14 +828,14 @@
     # and it defaults to an empty hash.
     # If +initheader+ doesn't have the key 'accept-encoding', then
     # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used,
-    # so that gzip compression is used in preference to deflate 
-    # compression, which is used in preference to no compression. 
+    # so that gzip compression is used in preference to deflate
+    # compression, which is used in preference to no compression.
     # Ruby doesn't have libraries to support the compress (Lempel-Ziv)
     # compression, so that is not supported.  The intent of this is
     # to reduce bandwidth by default.   If this routine sets up
     # compression, then it does the decompression also, removing
     # the header as well to prevent confusion.  Otherwise
-    # it leaves the body as it found it. 
+    # it leaves the body as it found it.
     #
     # In version 1.1 (ruby 1.6), this method returns a pair of objects,
     # a Net::HTTPResponse object and the entity body string.
@@ -784,7 +850,7 @@
     # +dest+ argument is obsolete.
     # It still works but you must not use it.
     #
-    # In version 1.1, this method might raise an exception for 
+    # In version 1.1, this method might raise an exception for
     # 3xx (redirect). In this case you can get a HTTPResponse object
     # by "anException.response".
     #
@@ -795,7 +861,7 @@
     #
     #     # version 1.2 (bundled with Ruby 1.8 or later)
     #     response = http.get('/index.html')
-    #     
+    #
     #     # using block
     #     File.open('result.txt', 'w') {|f|
     #       http.get('/~foo/') do |str|
@@ -824,7 +890,7 @@
             r.delete("content-encoding")
           when "identity"
             ; # nothing needed
-          else 
+          else
             ; # Don't do anything dramatic, unless we need to later
           end
         else
@@ -842,21 +908,21 @@
 
     # Gets only the header from +path+ on the connected-to host.
     # +header+ is a Hash like { 'Accept' => '*/*', ... }.
-    # 
+    #
     # This method returns a Net::HTTPResponse object.
-    # 
-    # In version 1.1, this method might raise an exception for 
+    #
+    # In version 1.1, this method might raise an exception for
     # 3xx (redirect). On the case you can get a HTTPResponse object
     # by "anException.response".
     # In version 1.2, this method never raises an exception.
-    # 
+    #
     #     response = nil
     #     Net::HTTP.start('some.www.server', 80) {|http|
     #       response = http.head('/index.html')
     #     }
     #     p response['content-type']
     #
-    def head(path, initheader = nil) 
+    def head(path, initheader = nil)
       res = request(Head.new(path, initheader))
       res.value unless @newimpl
       res
@@ -864,11 +930,11 @@
 
     # Posts +data+ (must be a String) to +path+. +header+ must be a Hash
     # like { 'Accept' => '*/*', ... }.
-    # 
+    #
     # In version 1.1 (ruby 1.6), this method returns a pair of objects, a
     # Net::HTTPResponse object and an entity body string.
     # In version 1.2 (ruby 1.8), this method returns a Net::HTTPResponse object.
-    # 
+    #
     # If called with a block, yields each fragment of the
     # entity body in turn as a string as it are read from
     # the socket.  Note that in this case, the returned response
@@ -876,18 +942,18 @@
     #
     # +dest+ argument is obsolete.
     # It still works but you must not use it.
-    # 
-    # In version 1.1, this method might raise an exception for 
+    #
+    # In version 1.1, this method might raise an exception for
     # 3xx (redirect). In this case you can get an HTTPResponse object
     # by "anException.response".
     # In version 1.2, this method never raises exception.
-    # 
+    #
     #     # version 1.1
     #     response, body = http.post('/cgi-bin/search.rb', 'query=foo')
-    # 
+    #
     #     # version 1.2
     #     response = http.post('/cgi-bin/search.rb', 'query=foo')
-    # 
+    #
     #     # using block
     #     File.open('result.txt', 'w') {|f|
     #       http.post('/cgi-bin/search.rb', 'query=foo') do |str|
@@ -980,21 +1046,21 @@
 
     # Sends a GET request to the +path+ and gets a response,
     # as an HTTPResponse object.
-    # 
+    #
     # When called with a block, yields an HTTPResponse object.
     # The body of this response will not have been read yet;
     # the caller can process it using HTTPResponse#read_body,
     # if desired.
     #
     # Returns the response.
-    # 
+    #
     # This method never raises Net::* exceptions.
-    # 
+    #
     #     response = http.request_get('/index.html')
     #     # The entity body is already read here.
     #     p response['content-type']
     #     puts response.body
-    # 
+    #
     #     # using block
     #     http.request_get('/index.html') {|response|
     #       p response['content-type']
@@ -1011,9 +1077,9 @@
     # as an HTTPResponse object.
     #
     # Returns the response.
-    # 
+    #
     # This method never raises Net::* exceptions.
-    # 
+    #
     #     response = http.request_head('/index.html')
     #     p response['content-type']
     #
@@ -1023,21 +1089,21 @@
 
     # Sends a POST request to the +path+ and gets a response,
     # as an HTTPResponse object.
-    # 
+    #
     # When called with a block, yields an HTTPResponse object.
     # The body of this response will not have been read yet;
     # the caller can process it using HTTPResponse#read_body,
     # if desired.
     #
     # Returns the response.
-    # 
+    #
     # This method never raises Net::* exceptions.
-    # 
+    #
     #     # example
     #     response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...')
     #     p response.status
     #     puts response.body          # body is already read
-    # 
+    #
     #     # using block
     #     http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response|
     #       p response.status
@@ -1065,7 +1131,7 @@
     # This method also sends DATA string if DATA is given.
     #
     # Returns a HTTPResponse object.
-    # 
+    #
     # This method never raises Net::* exceptions.
     #
     #    response = http.send_request('GET', '/index.html')
@@ -1079,14 +1145,14 @@
     # Sends an HTTPRequest object REQUEST to the HTTP server.
     # This method also sends DATA string if REQUEST is a post/put request.
     # Giving DATA for get/head request causes ArgumentError.
-    # 
+    #
     # When called with a block, yields an HTTPResponse object.
     # The body of this response will not have been read yet;
     # the caller can process it using HTTPResponse#read_body,
     # if desired.
     #
     # Returns a HTTPResponse object.
-    # 
+    #
     # This method never raises Net::* exceptions.
     #
     def request(req, body = nil, &block)  # :yield: +response+
@@ -1121,6 +1187,10 @@
       }
       end_transport req, res
       res
+    rescue => exception
+      D "Conn close because of error #{exception}"
+      @socket.close unless @socket.closed?
+      raise exception
     end
 
     def begin_transport(req)
@@ -1289,15 +1359,17 @@
     end
 
     # Returns the header field corresponding to the case-insensitive key.
-    # Returns the default value +args+, or the result of the block, or nil,
-    # if there's no header field named key.  See Hash#fetch
+    # Returns the default value +args+, or the result of the block, or
+    # raises an IndexErrror if there's no header field named +key+
+    # See Hash#fetch
     def fetch(key, *args, &block)   #:yield: +key+
       a = @header.fetch(key.downcase, *args, &block)
-      a.join(', ')
+      a.kind_of?(Array) ? a.join(', ') : a
     end
 
     # Iterates for each header names and values.
     def each_header   #:yield: +key+, +value+
+      block_given? or return enum_for(__method__)
       @header.each do |k,va|
         yield k, va.join(', ')
       end
@@ -1307,13 +1379,15 @@
 
     # Iterates for each header names.
     def each_name(&block)   #:yield: +key+
+      block_given? or return enum_for(__method__)
       @header.each_key(&block)
     end
 
     alias each_key each_name
 
     # Iterates for each capitalized header names.
-    def each_capitalized_name(&block)   #:yield: +key+
+    def each_capitalized_name  #:yield: +key+
+      block_given? or return enum_for(__method__)
       @header.each_key do |k|
         yield capitalize(k)
       end
@@ -1321,6 +1395,7 @@
 
     # Iterates for each header values.
     def each_value   #:yield: +value+
+      block_given? or return enum_for(__method__)
       @header.each_value do |va|
         yield va.join(', ')
       end
@@ -1343,6 +1418,7 @@
 
     # As for #each_header, except the keys are provided in capitalized form.
     def each_capitalized
+      block_given? or return enum_for(__method__)
       @header.each do |k,v|
         yield capitalize(k), v.join(', ')
       end
@@ -1418,7 +1494,7 @@
           raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
       len.to_i
     end
-    
+
     def content_length=(len)
       unless len
         @header.delete 'content-length'
@@ -1428,7 +1504,7 @@
     end
 
     # Returns "true" if the "transfer-encoding" header is present and
-    # set to "chunked".  This is an HTTP/1.1 feature, allowing the 
+    # set to "chunked".  This is an HTTP/1.1 feature, allowing the
     # the content to be sent in "chunks" without at the outset
     # stating the entire content length.
     def chunked?
@@ -1444,13 +1520,13 @@
       return nil unless @header['content-range']
       m = %r<bytes\s+(\d+)-(\d+)/(\d+|\*)>i.match(self['Content-Range']) or
           raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
-      m[1].to_i .. m[2].to_i + 1
+      m[1].to_i .. m[2].to_i
     end
 
     # The length of the range represented in Content-Range: header.
     def range_length
       r = content_range() or return nil
-      r.end - r.begin
+      r.end - r.begin + 1
     end
 
     # Returns a content type string such as "text/html".
@@ -1469,7 +1545,7 @@
       return nil unless @header['content-type']
       self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip
     end
-    
+
     # Returns a content type string such as "html".
     # This method returns nil if Content-Type: header field does not exist
     # or sub-type is not given (e.g. "Content-Type: text").
@@ -1512,7 +1588,7 @@
     #    http.form_data = {"q" => "ruby", "lang" => "en"}
     #    http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"}
     #    http.set_form_data({"q" => "ruby", "lang" => "en"}, ';')
-    #    
+    #
     def set_form_data(params, sep = '&')
       self.body = params.map {|k, v| encode_kvpair(k, v) }.flatten.join(sep)
       self.content_type = 'application/x-www-form-urlencoded'
@@ -1521,7 +1597,7 @@
     alias form_data= set_form_data
 
     def encode_kvpair(k, vs)
-      Array(vs).map {|v| "#{urlencode(k)}=#{urlencode(v.to_s)}" }
+      Array(vs).map {|v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }
     end
     private :encode_kvpair
 
@@ -1688,14 +1764,14 @@
       buf << "\r\n"
       sock.write buf
     end
-  
+
   end
 
 
-  # 
+  #
   # HTTP request class. This class wraps request header and entity path.
   # You *must* use its subclass, Net::HTTP::Get, Post, Head.
-  # 
+  #
   class HTTPRequest < HTTPGenericRequest
 
     # Creates HTTP request object.
@@ -1835,17 +1911,17 @@
   # HTTP response class. This class wraps response header and entity.
   # Mixes in the HTTPHeader module, which provides access to response
   # header values both via hash-like methods and individual readers.
-  # Note that each possible HTTP response code defines its own 
+  # Note that each possible HTTP response code defines its own
   # HTTPResponse subclass.  These are listed below.
   # All classes are
   # defined under the Net module. Indentation indicates inheritance.
-  # 
+  #
   #   xxx        HTTPResponse
-  # 
+  #
   #     1xx        HTTPInformation
-  #       100        HTTPContinue    
+  #       100        HTTPContinue
   #       101        HTTPSwitchProtocol
-  # 
+  #
   #     2xx        HTTPSuccess
   #       200        HTTPOK
   #       201        HTTPCreated
@@ -1854,7 +1930,7 @@
   #       204        HTTPNoContent
   #       205        HTTPResetContent
   #       206        HTTPPartialContent
-  # 
+  #
   #     3xx        HTTPRedirection
   #       300        HTTPMultipleChoice
   #       301        HTTPMovedPermanently
@@ -1863,7 +1939,7 @@
   #       304        HTTPNotModified
   #       305        HTTPUseProxy
   #       307        HTTPTemporaryRedirect
-  # 
+  #
   #     4xx        HTTPClientError
   #       400        HTTPBadRequest
   #       401        HTTPUnauthorized
@@ -1883,7 +1959,7 @@
   #       415        HTTPUnsupportedMediaType
   #       416        HTTPRequestedRangeNotSatisfiable
   #       417        HTTPExpectationFailed
-  # 
+  #
   #     5xx        HTTPServerError
   #       500        HTTPInternalServerError
   #       501        HTTPNotImplemented
@@ -1891,7 +1967,7 @@
   #       503        HTTPServiceUnavailable
   #       504        HTTPGatewayTimeOut
   #       505        HTTPVersionNotSupported
-  # 
+  #
   #     xxx        HTTPUnknownResponse
   #
   class HTTPResponse
@@ -2145,19 +2221,26 @@
       end
 
       def each_response_header(sock)
+        key = value = nil
         while true
           line = sock.readuntil("\n", true).sub(/\s+\z/, '')
           break if line.empty?
-          m = /\A([^:]+):\s*/.match(line) or
-              raise HTTPBadResponse, 'wrong header line format'
-          yield m[1], m.post_match
+          if line[0] == ?\s or line[0] == ?\t and value
+            value << ' ' unless value.empty?
+            value << line.strip
+          else
+            yield key, value if key
+            key, value = line.strip.split(/\s*:\s*/, 2)
+            raise HTTPBadResponse, 'wrong header line format' if value.nil?
+          end
         end
+        yield key, value if key
       end
     end
 
     # next is to fix bug in RDoc, where the private inside class << self
     # spills out.
-    public 
+    public
 
     include HTTPHeader
 
@@ -2190,7 +2273,7 @@
     # To allow Net::HTTP 1.1 style assignment
     # e.g.
     #    response, body = Net::HTTP.get(....)
-    # 
+    #
     def to_ary
       warn "net/http.rb: warning: Net::HTTP v1.1 style assignment found at #{caller(1)[0]}; use `response = http.get(...)' instead." if $VERBOSE
       res = self.dup
@@ -2350,8 +2433,12 @@
             raise HTTPBadResponse, "wrong chunk size line: #{line}"
         len = hexlen.hex
         break if len == 0
-        @socket.read len, dest; total += len
-        @socket.read 2   # \r\n
+        begin
+          @socket.read len, dest
+        ensure
+          total += len
+          @socket.read 2   # \r\n
+        end
       end
       until @socket.readline.empty?
         # none
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100512/2c71e492/attachment-0001.html>


More information about the macruby-changes mailing list