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/trunk@62959 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2018-03-28 08:06:18 +00:00
Родитель d6c0b3d787
Коммит 4639ac8953
1 изменённых файлов: 31 добавлений и 15 удалений

Просмотреть файл

@ -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,28 +120,20 @@ module WEBrick
if ranges.size > 1
time = Time.now
boundary = "#{time.sec}_#{time.usec}_#{Process::pid}"
body = ''
parts = []
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
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}"
if req.http_version < '1.1'
res['connection'] = 'close'
else
res.chunked = true
end
res.body = body
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