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

ruby-changes:50800

From: naruse <ko1@a...>
Date: Wed, 28 Mar 2018 21:51:03 +0900 (JST)
Subject: [ruby-changes:50800] naruse:r63006 (ruby_2_5): merge revision(s) 62953-62959:

naruse	2018-03-28 21:50:56 +0900 (Wed, 28 Mar 2018)

  New Revision: 63006

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=63006

  Log:
    merge revision(s) 62953-62959:
    
    win32/file.c: relative path with drive letter
    
    * win32/file.c (IS_ABSOLUTE_PATH_P): home directory should not be
      a relative path regardless a drive letter.   PathIsRelativeW
      returns FALSE on such path.  [ruby-core:86356] [Bug #14638]
    ------------------------------------------------------------------------
    r62953 | normal | 2018-03-28 17:05:46 +0900 (Wed, 28 Mar 2018) | 15 lines
    
    webrick: favor .write over << method
    
    This will make the next change to use IO.copy_stream
    easier-to-read.  When we can drop Ruby 2.4 support in a few
    years, this will allow us to use writev(2) with multiple
    arguments for headers and chunked responses.
    
    * lib/webrick/cgi.rb (write): new wrapper method
      lib/webrick/httpresponse.rb: (send_header): use socket.write
      (send_body_io): ditto
      (send_body_string): ditto
      (send_body_proc): ditto
      (_write_data): ditto
      (ChunkedWrapper#write): ditto
      (_send_file): ditto
    ------------------------------------------------------------------------
    r62954 | normal | 2018-03-28 17:05:52 +0900 (Wed, 28 Mar 2018) | 14 lines
    
    webrick/httpresponse: IO.copy_stream for regular files
    
    Remove the redundant _send_file method since its functionality
    is unnecessary with IO.copy_stream.  IO.copy_stream also allows
    the use of sendfile under some OSes to speed up copies to
    non-TLS sockets.
    
    Testing with "curl >/dev/null" and "ruby -run -e httpd" to
    read a 1G file over Linux loopback reveals a reduction from
    around ~0.770 to ~0.490 seconds on the client side.
    
    * lib/webrick/httpresponse.rb (send_body_io): use IO.copy_stream
      (_send_file): remove
      [Feature #14237]
    ------------------------------------------------------------------------
    r62955 | normal | 2018-03-28 17:05:57 +0900 (Wed, 28 Mar 2018) | 10 lines
    
    webrick: use IO.copy_stream for single range response
    
    This is also compatible with range responses generated
    by Rack::File (tested with rack 2.0.3).
    
    * lib/webrick/httpresponse.rb (send_body_io): use Content-Range
    * lib/webrick/httpservlet/filehandler.rb (make_partial_content):
      use File object for the single range case
    * test/webrick/test_filehandler.rb (get_res_body): use send_body
      to test result
    ------------------------------------------------------------------------
    r62956 | normal | 2018-03-28 17:06:02 +0900 (Wed, 28 Mar 2018) | 7 lines
    
    test/webrick/test_filehandler.rb: stricter multipart range test
    
    We need to ensure we generate compatibile output in
    the face of future changes
    
    * test/webrick/test_filehandler.rb (test_make_partial_content):
      check response body
    ------------------------------------------------------------------------
    r62957 | normal | 2018-03-28 17:06:08 +0900 (Wed, 28 Mar 2018) | 8 lines
    
    webrick: quiet warning for multi-part ranges
    
    Content-Length is ignored by WEBrick::HTTPResponse even if we
    calculate it, so instead we chunk responses to HTTP/1.1 clients
    and terminate HTTP/1.0 connections.
    
    * lib/webrick/httpservlet/filehandler.rb (make_partial_content):
      quiet warning
    ------------------------------------------------------------------------
    r62958 | normal | 2018-03-28 17:06:13 +0900 (Wed, 28 Mar 2018) | 7 lines
    
    webrick/httpresponse: make ChunkedWrapper copy_stream-compatible
    
    The .write method needs to return the number of bytes written
    to avoid confusing IO.copy_stream.
    
    * lib/webrick/httpresponse.rb (ChunkedWrapper#write): return bytes written
      (ChunkedWrapper#<<): return self
    ------------------------------------------------------------------------
    r62959 | normal | 2018-03-28 17:06:18 +0900 (Wed, 28 Mar 2018) | 9 lines
    
    webrick: use IO.copy_stream for multipart response
    
    Use the new Proc response body feature to generate a multipart
    range response dynamically.  We use a flat array to minimize
    object overhead as much as possible; as many ranges may fit
    into an HTTP request header.
    
    * lib/webrick/httpservlet/filehandler.rb (multipart_body): new method
      (make_partial_content): use multipart_body

  Modified directories:
    branches/ruby_2_5/
  Modified files:
    branches/ruby_2_5/lib/webrick/cgi.rb
    branches/ruby_2_5/lib/webrick/httpresponse.rb
    branches/ruby_2_5/lib/webrick/httpservlet/filehandler.rb
    branches/ruby_2_5/test/webrick/test_filehandler.rb
    branches/ruby_2_5/version.h
Index: ruby_2_5/lib/webrick/httpresponse.rb
===================================================================
--- ruby_2_5/lib/webrick/httpresponse.rb	(revision 63005)
+++ ruby_2_5/lib/webrick/httpresponse.rb	(revision 63006)
@@ -295,7 +295,7 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpresponse.rb#L295
           data << "Set-Cookie: " << check_header(cookie.to_s) << CRLF
         }
         data << CRLF
-        _write_data(socket, data)
+        socket.write(data)
       end
     rescue InvalidHeader => e
       @header.clear
@@ -416,18 +416,24 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpresponse.rb#L416
             @body.readpartial(@buffer_size, buf)
             size = buf.bytesize
             data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
-            _write_data(socket, data)
+            socket.write(data)
             data.clear
             @sent_size += size
           rescue EOFError
             break
           end while true
           buf.clear
-          _write_data(socket, "0#{CRLF}#{CRLF}")
+          socket.write("0#{CRLF}#{CRLF}")
         else
-          size = @header['content-length'].to_i
-          _send_file(socket, @body, 0, size)
-          @sent_size = size
+          if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ @header['content-range']
+            offset = $1.to_i
+            size = $2.to_i - offset + 1
+          else
+            offset = nil
+            size = @header['content-length']
+            size = size.to_i if size
+          end
+          @sent_size = IO.copy_stream(@body, socket, size, offset)
         end
       ensure
         @body.close
@@ -444,13 +450,13 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpresponse.rb#L450
           size = buf.bytesize
           data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
           buf.clear
-          _write_data(socket, data)
+          socket.write(data)
           @sent_size += size
         end
-        _write_data(socket, "0#{CRLF}#{CRLF}")
+        socket.write("0#{CRLF}#{CRLF}")
       else
         if @body && @body.bytesize > 0
-          _write_data(socket, @body)
+          socket.write(@body)
           @sent_size = @body.bytesize
         end
       end
@@ -461,7 +467,7 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpresponse.rb#L467
         # do nothing
       elsif chunked?
         @body.call(ChunkedWrapper.new(socket, self))
-        _write_data(socket, "0#{CRLF}#{CRLF}")
+        socket.write("0#{CRLF}#{CRLF}")
       else
         size = @header['content-length'].to_i
         @body.call(socket)
@@ -476,40 +482,25 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpresponse.rb#L482
       end
 
       def write(buf)
-        return if buf.empty?
+        return 0 if buf.empty?
         socket = @socket
         @resp.instance_eval {
           size = buf.bytesize
           data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}"
-          _write_data(socket, data)
+          socket.write(data)
           data.clear
           @sent_size += size
+          size
         }
       end
-      alias :<< :write
-    end
-
-    def _send_file(output, input, offset, size)
-      while offset > 0
-        sz = @buffer_size < size ? @buffer_size : size
-        buf = input.read(sz)
-        offset -= buf.bytesize
-      end
 
-      if size == 0
-        while buf = input.read(@buffer_size)
-          _write_data(output, buf)
-        end
-      else
-        while size > 0
-          sz = @buffer_size < size ? @buffer_size : size
-          buf = input.read(sz)
-          _write_data(output, buf)
-          size -= buf.bytesize
-        end
+      def <<(*buf)
+        write(buf)
+        self
       end
     end
 
+    # preserved for compatibility with some 3rd-party handlers
     def _write_data(socket, data)
       socket << data
     end
Index: ruby_2_5/lib/webrick/cgi.rb
===================================================================
--- ruby_2_5/lib/webrick/cgi.rb	(revision 63005)
+++ ruby_2_5/lib/webrick/cgi.rb	(revision 63006)
@@ -265,6 +265,10 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/cgi.rb#L265
         @out_port << data
       end
 
+      def write(data)
+        @out_port.write(data)
+      end
+
       def cert
         return nil unless defined?(OpenSSL)
         if pem = @env["SSL_SERVER_CERT"]
Index: ruby_2_5/lib/webrick/httpservlet/filehandler.rb
===================================================================
--- ruby_2_5/lib/webrick/httpservlet/filehandler.rb	(revision 63005)
+++ ruby_2_5/lib/webrick/httpservlet/filehandler.rb	(revision 63006)
@@ -86,6 +86,30 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpservlet/filehandler.rb#L86
         return false
       end
 
+      # returns a lambda for webrick/httpresponse.rb send_body_proc
+      def multipart_body(body, parts, boundary, mtype, filesize)
+        lambda do |socket|
+          begin
+            begin
+              first = parts.shift
+              last = parts.shift
+              socket.write(
+                "--#{boundary}#{CRLF}" \
+                "Content-Type: #{mtype}#{CRLF}" \
+                "Content-Range: bytes #{first}-#{last}/#{filesize}#{CRLF}" \
+                "#{CRLF}"
+              )
+
+              IO.copy_stream(body, socket, last - first + 1, first)
+              socket.write(CRLF)
+            end while parts[0]
+            socket.write("--#{boundary}--#{CRLF}")
+          ensure
+            body.close
+          end
+        end
+      end
+
       def make_partial_content(req, res, filename, filesize)
         mtype = HTTPUtils::mime_type(filename, @config[:MimeTypes])
         unless ranges = HTTPUtils::parse_range_header(req['range'])
@@ -96,37 +120,27 @@ module WEBrick https://github.com/ruby/ruby/blob/trunk/ruby_2_5/lib/webrick/httpservlet/filehandler.rb#L120
           if ranges.size > 1
             time = Time.now
             boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
-            body = ''
-            ranges.each{|range|
-              first, last = prepare_range(range, filesize)
-              next if first < 0
-              io.pos = first
-              content = io.read(last-first+1)
-              body << "--" << boundary << CRLF
-              body << "Content-Type: #{mtype}" << CRLF
-              body << "Content-Range: bytes #{first}-#{last}/#{filesize}" << CRLF
-              body << CRLF
-              body << content
-              body << CRLF
+            parts = []
+            ranges.each {|range|
+              prange = prepare_range(range, filesize)
+              next if prange[0] < 0
+              parts.concat(prange)
             }
-            raise HTTPStatus::RequestRangeNotSatisfiable if body.empty?
-            body << "--" << boundary << "--" << CRLF
+            raise HTTPStatus::RequestRangeNotSatisfiable if parts.empty?
             res["content-type"] = "multipart/byteranges; boundary=#{boundary}"
-            res.body = body
+            if req.http_version < '1.1'
+              res['connection'] = 'close'
+            else
+              res.chunked = true
+            end
+            res.body = multipart_body(io.dup, parts, boundary, mtype, filesize)
           elsif range = ranges[0]
             first, last = prepare_range(range, filesize)
             raise HTTPStatus::RequestRangeNotSatisfiable if first < 0
-            if last == filesize - 1
-              content = io.dup
-              content.pos = first
-            else
-              io.pos = first
-              content = io.read(last-first+1)
-            end
             res['content-type'] = mtype
             res['content-range'] = "bytes #{first}-#{last}/#{filesize}"
             res['content-length'] = last - first + 1
-            res.body = content
+            res.body = io.dup
           else
             raise HTTPStatus::BadRequest
           end
Index: ruby_2_5/version.h
===================================================================
--- ruby_2_5/version.h	(revision 63005)
+++ ruby_2_5/version.h	(revision 63006)
@@ -1,6 +1,6 @@ https://github.com/ruby/ruby/blob/trunk/ruby_2_5/version.h#L1
 #define RUBY_VERSION "2.5.1"
 #define RUBY_RELEASE_DATE "2018-03-28"
-#define RUBY_PATCHLEVEL 53
+#define RUBY_PATCHLEVEL 54
 
 #define RUBY_RELEASE_YEAR 2018
 #define RUBY_RELEASE_MONTH 3
Index: ruby_2_5/test/webrick/test_filehandler.rb
===================================================================
--- ruby_2_5/test/webrick/test_filehandler.rb	(revision 63005)
+++ ruby_2_5/test/webrick/test_filehandler.rb	(revision 63006)
@@ -20,16 +20,10 @@ class WEBrick::TestFileHandler < Test::U https://github.com/ruby/ruby/blob/trunk/ruby_2_5/test/webrick/test_filehandler.rb#L20
   end
 
   def get_res_body(res)
-    body = res.body
-    if defined? body.read
-      begin
-        body.read
-      ensure
-        body.close
-      end
-    else
-      body
-    end
+    sio = StringIO.new
+    sio.binmode
+    res.send_body(sio)
+    sio.string
   end
 
   def make_range_request(range_spec)
@@ -81,6 +75,23 @@ class WEBrick::TestFileHandler < Test::U https://github.com/ruby/ruby/blob/trunk/ruby_2_5/test/webrick/test_filehandler.rb#L75
 
     res = make_range_response(filename, "bytes=0-0, -2")
     assert_match(%r{^multipart/byteranges}, res["content-type"])
+    body = get_res_body(res)
+    boundary = /; boundary=(.+)/.match(res['content-type'])[1]
+    off = filesize - 2
+    last = filesize - 1
+
+    exp = "--#{boundary}\r\n" \
+          "Content-Type: text/plain\r\n" \
+          "Content-Range: bytes 0-0/#{filesize}\r\n" \
+          "\r\n" \
+          "#{IO.read(__FILE__, 1)}\r\n" \
+          "--#{boundary}\r\n" \
+          "Content-Type: text/plain\r\n" \
+          "Content-Range: bytes #{off}-#{last}/#{filesize}\r\n" \
+          "\r\n" \
+          "#{IO.read(__FILE__, 2, off)}\r\n" \
+          "--#{boundary}--\r\n"
+    assert_equal exp, body
   end
 
   def test_filehandler
Index: ruby_2_5
===================================================================
--- ruby_2_5	(revision 63005)
+++ ruby_2_5	(revision 63006)

Property changes on: ruby_2_5
___________________________________________________________________
Modified: svn:mergeinfo
## -0,0 +0,1 ##
   Merged /trunk:r62953-62959

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

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