summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authornaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-28 12:50:56 +0000
committernaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-03-28 12:50:56 +0000
commit84afd795b818062208e2148e8d08c8cdf75300ac (patch)
treeb54750cf19bfa7a4ca31a46ce8a6aaa1f03b49cf /lib
parent26f00c674898d9bdf8dc79ac8ada106a9793a34c (diff)
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 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_5@63006 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/webrick/cgi.rb4
-rw-r--r--lib/webrick/httpresponse.rb55
-rw-r--r--lib/webrick/httpservlet/filehandler.rb60
3 files changed, 64 insertions, 55 deletions
diff --git a/lib/webrick/cgi.rb b/lib/webrick/cgi.rb
index 94f385f1dd..33f1542731 100644
--- a/lib/webrick/cgi.rb
+++ b/lib/webrick/cgi.rb
@@ -265,6 +265,10 @@ module WEBrick
@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"]
diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb
index 323012d996..820579237f 100644
--- a/lib/webrick/httpresponse.rb
+++ b/lib/webrick/httpresponse.rb
@@ -295,7 +295,7 @@ module WEBrick
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
@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
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
# 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
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
diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb
index 2c02d0ffe7..e4d892ee66 100644
--- a/lib/webrick/httpservlet/filehandler.rb
+++ b/lib/webrick/httpservlet/filehandler.rb
@@ -86,6 +86,30 @@ module WEBrick
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
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