From: Albert Chou Date: 2002-11-26T13:00:00+09:00 Subject: socket bug? This is a multi-part message in MIME format. ------_=_NextPart_001_01C29500.057B2B23 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable I'm not sure where to look for the bug that I seem to have found in the socket library on Windows 2000. I've made some modifications to the Net module (at end of message), and I am executing the following code using it to send an HTTP GET request with a very long query string. host =3D 'localhost' port =3D 80 http =3D Net::HTTP.new( host, port ) request_method =3D 'GET' request_has_body =3D FALSE response_has_body =3D TRUE N =3D 320 path =3D '/index.html' + '?' + '%FF' * ( 41 * 1024 + N ) extra_headers =3D nil req =3D Net::HTTPGenericRequest.new( request_method, request_has_body, response_has_body, path, extra_headers ) post_data =3D nil test =3D FALSE response, =3D http.request( req, post_data, test ) I get the following error message from Ruby 1.7.3: C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:501:in `sysread': Invalid argument (Errno::EINVAL) from C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:501:in `rbuf_fill' from C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:477:in `readuntil' from C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:488:in `readline' from C:/Progra~1/Ruby/lib/ruby/1.7/net/http.rb:1415:in `read_status_line' from C:/Progra~1/Ruby/lib/ruby/1.7/net/http.rb:1399:in `read_new' from C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:115:in `request' from C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:108:in `request' from C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:106:in `start' from C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:109:in `request' from C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:441 Ruby 1.6.7 gives a similar but not identical set of error messages: C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:5 76:in `write': Invalid argument (Errno::EINVAL) from C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:5 76:in `do_write' from C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:5 59:in `writeline' from C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:5 59:in `writing' from C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:5 59:in `writeline' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:246:in `request' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:194:in `exec' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:113:in `request' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:108:in `request' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:107:in `start' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:107:in `request' from C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:449 When the value of the variable "N" is less than 320, the sending of the request works OK, but the error behavior occurs for N >=3D 320 for both Ruby 1.7.3 and 1.6.7. I had hoped Ruby would be able to construct and send query strings and POST request bodies of arbitrary size for boundary case testing of our product, but I'm running up against this error, which I don't really know what to do about. Any ideas out there? Thanks in advance, Al module Net class HTTP alias :old_request :request # Modification: add "test" parameter. def request( req, body =3D nil, test =3D FALSE, &block ) unless started? then start { req['connection'] =3D 'close' return request(req, body, test, &block) } end begin_transport req req.exec @socket, @curr_http_version, edit_path(req.path), body, test begin res =3D HTTPResponse.read_new(@socket) end while HTTPContinue =3D=3D=3D res res.reading_body(@socket, req.response_body_permitted?) { yield res if block_given? } end_transport req, res res end attr_accessor :curr_http_version end module HTTPHeader # Change behavior: do not set header names to lowercase. alias :old_writer :[]=3D def []=3D( key, val ) @header[ key.downcase ] =3D val @header_array << [key, val] end end class HTTPResponse alias :old_initialize :initialize def initialize( httpv, code, msg ) @http_version =3D httpv @code =3D code @message =3D msg @header =3D {} @body =3D nil @read =3D false @header_array =3D Array.new end attr_reader :header_array end class HTTPGenericRequest attr_accessor :header # Add header_array instance variable. alias :old_initialize :initialize def initialize( m, reqbody, resbody, path, initheader =3D nil ) @method =3D m @request_has_body =3D reqbody @response_has_body =3D resbody @path =3D path @header =3D tmp =3D {} @header_array =3D Array.new return unless initheader initheader.each do |k,v| key =3D k.downcase if tmp.key? key then $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE end tmp[ key ] =3D v.strip end tmp['accept'] ||=3D '*/*' end alias :old_exec :exec # Modification: add "test" parameter. def exec( sock, ver, path, body, test =3D FALSE ) if body then raise ArgumentError, 'HTTP request body is not premitted' unless request_body_permitted? send_request_with_body sock, ver, path, body, test else request sock, ver, path, test end end alias :old_send_request_with_body :send_request_with_body # Modification: add "test" parameter. def send_request_with_body( sock, ver, path, body, test =3D FALSE ) if block_given? then ac =3D Accumulator.new yield ac # must be yield, DO NOT USE block.call data =3D ac.terminate else data =3D body end # Modification: only calculate and set Content-Length if its value hasn't already been specified. unless @header['content-length'] @header['content-length'] =3D data.size.to_s @header.delete 'transfer-encoding' end # Modification: only set Content-Type if its value hasn't already been specified. unless @header['content-type'] $stderr.puts 'Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE @header['content-type'] =3D 'application/x-www-form-urlencoded' end # Modification: add "test" argument. request sock, ver, path, test # Modification: add if statement. if test then puts data exit else sock.write data end end alias :old_request :request def request( sock, ver, path, test =3D FALSE ) # Modification: add "test" option. if test then sprintf('%s %s HTTP/%s', @method, path, ver) canonical_each do |k,v| puts k + ': ' + v end puts '' else sock.writeline sprintf('%s %s HTTP/%s', @method, path, ver) canonical_each do |k,v| sock.writeline k + ': ' + v end sock.writeline '' end end end end --=20 Albert Davidson Chou, QA Manager TeaLeaf Technology, Inc. (415) 932-5031 AChou@TeaLeaf.com | http://www.TeaLeaf.com/ Evaluate TeaLeaf Technology today: http://www.TeaLeaf.com/demo/ ------_=_NextPart_001_01C29500.057B2B23 Content-Type: text/html; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable socket bug?

I'm not sure where to look for the bug that I seem to = have found in the socket library on Windows 2000.  I've made some = modifications to the Net module (at end of message), and I am executing = the following code using it to send an HTTP GET request with a very long = query string.

host =3D 'localhost'
port =3D 80
http =3D Net::HTTP.new( host, port )

request_method =3D 'GET'
request_has_body =3D FALSE
response_has_body =3D TRUE
N =3D 320
path =3D '/index.html' + '?' + '%FF' * ( 41 * 1024 + = N )
extra_headers =3D nil
req =3D Net::HTTPGenericRequest.new( request_method, = request_has_body, response_has_body, path, extra_headers )

post_data =3D nil
test =3D FALSE
response, =3D http.request( req, post_data, test = )



I get the following error message from Ruby = 1.7.3:

C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:501:in = `sysread': Invalid argument
 (Errno::EINVAL)
        from = C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:501:in `rbuf_fill'
        from = C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:477:in `readuntil'
        from = C:/Progra~1/Ruby/lib/ruby/1.7/net/protocol.rb:488:in `readline'
        from = C:/Progra~1/Ruby/lib/ruby/1.7/net/http.rb:1415:in = `read_status_line'
        from = C:/Progra~1/Ruby/lib/ruby/1.7/net/http.rb:1399:in `read_new'
        from = C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:115:in = `request'
        from = C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:108:in = `request'
        from = C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:106:in = `start'
        from = C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:109:in = `request'
        from = C:/Perforce_workspace/2.0.5/regress/bin/send_HTTP_request.rb:441


Ruby 1.6.7 gives a similar but not identical set of = error messages:

C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/proto= col.rb:576:in `write': Invalid argument (Errno::EINVAL)
        from = C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:57= 6:in `do_write'
        from = C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:55= 9:in `writeline'
        from = C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:55= 9:in `writing'
        from = C:/Perforce_workspace/2.0.5/nt/tools/Ruby/lib/ruby/1.6/net/protocol.rb:55= 9:in `writeline'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:246:in = `request'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:194:in = `exec'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:113:in = `request'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:108:in = `request'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:107:in = `start'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:107:in = `request'
        from = C:\Perforce_workspace\2.0.5\regress\bin\send_HTTP_request_TEST.rb:449


When the value of the variable "N" is less = than 320, the sending of the request works OK, but the error behavior = occurs for N >=3D 320 for both Ruby 1.7.3 and 1.6.7.  I had = hoped Ruby would be able to construct and send query strings and POST = request bodies of arbitrary size for boundary case testing of our = product, but I'm running up against this error, which I don't really = know what to do about.  Any ideas out there?


Thanks in advance,
Al



module Net
  class HTTP
    alias :old_request :request
    # Modification:  add = "test" parameter.
    def request( req, body =3D nil, = test =3D FALSE, &block )
      unless started? = then
        start = {
          = req['connection'] =3D 'close'
          return = request(req, body, test, &block)
        }
      end

      begin_transport = req
      req.exec @socket, = @curr_http_version, edit_path(req.path), body, test
      begin
        res =3D = HTTPResponse.read_new(@socket)
      end while HTTPContinue = =3D=3D=3D res
      = res.reading_body(@socket, req.response_body_permitted?) {
        yield res = if block_given?
      }
      end_transport req, = res

      res
    end

    attr_accessor = :curr_http_version
  end

  module HTTPHeader
    # Change behavior:  do not = set header names to lowercase.
    alias :old_writer :[]=3D
    def []=3D( key, val )
      @header[ key.downcase = ] =3D val
      @header_array << = [key, val]
    end
  end

  class HTTPResponse
    alias :old_initialize = :initialize
    def initialize( httpv, code, msg = )
      @http_version =3D = httpv
      = @code         =3D code
      = @message      =3D msg

      @header =3D {}
      @body =3D nil
      @read =3D false

      @header_array =3D = Array.new
    end

    attr_reader :header_array
  end

  class HTTPGenericRequest
    attr_accessor :header

    # Add header_array instance = variable.
    alias :old_initialize = :initialize
    def initialize( m, reqbody, = resbody, path, initheader =3D nil )
      @method =3D m
      @request_has_body =3D = reqbody
      @response_has_body =3D = resbody
      @path =3D path

      @header =3D tmp =3D = {}
      @header_array =3D = Array.new

      return unless = initheader
      initheader.each do = |k,v|
        key =3D = k.downcase
        if = tmp.key? key then
          = $stderr.puts "WARNING: duplicated HTTP header: #{k}" if = $VERBOSE
        end
        tmp[ key ] = =3D v.strip
      end
      tmp['accept'] ||=3D = '*/*'
    end

    alias :old_exec :exec
    # Modification:  add = "test" parameter.
    def exec( sock, ver, path, body, = test =3D FALSE )
      if body then
        raise = ArgumentError, 'HTTP request body is not premitted' unless = request_body_permitted?
        = send_request_with_body sock, ver, path, body, test
      else
        request = sock, ver, path, test
      end
    end

    alias :old_send_request_with_body = :send_request_with_body
    # Modification:  add = "test" parameter.
    def send_request_with_body( sock, = ver, path, body, test =3D FALSE )
      if block_given? = then
        ac =3D = Accumulator.new
        yield = ac            = ;  # must be yield, DO NOT USE block.call
        data =3D = ac.terminate
      else
        data =3D = body
      end

      # Modification:  = only calculate and set Content-Length if its value hasn't already been = specified.
      unless = @header['content-length']
        = @header['content-length'] =3D data.size.to_s
        = @header.delete 'transfer-encoding'
      end

      # Modification:  = only set Content-Type if its value hasn't already been specified.
      unless = @header['content-type']
        = $stderr.puts 'Content-Type did not set; using = application/x-www-form-urlencoded' if $VERBOSE
        = @header['content-type'] =3D 'application/x-www-form-urlencoded'
      end

      # Modification:  = add "test" argument.
      request sock, ver, = path, test
      # Modification:  = add if statement.
      if test then
        puts = data
        = exit
      else
        sock.write = data
      end
    end

    alias :old_request :request
    def request( sock, ver, path, test = =3D FALSE )
      # Modification:  = add "test" option.
      if test then
        = sprintf('%s %s HTTP/%s', @method, path, ver)
        = canonical_each do |k,v|
          puts k + = ': ' + v
        end
        puts = ''
      else
        = sock.writeline sprintf('%s %s HTTP/%s', @method, path, ver)
        = canonical_each do |k,v|
          = sock.writeline k + ': ' + v
        end
        = sock.writeline ''
      end
    end
  end
end
--
Albert Davidson Chou, QA Manager
TeaLeaf Technology, Inc.
(415) 932-5031
AChou@TeaLeaf.com | http://www.TeaLeaf.com/

  Evaluate TeaLeaf Technology today:
  http://www.TeaLeaf.com/demo/

------_=_NextPart_001_01C29500.057B2B23--