diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/webrick/httpresponse.rb | 68 | ||||
-rw-r--r-- | lib/webrick/httpservlet/filehandler.rb | 65 |
2 files changed, 91 insertions, 42 deletions
diff --git a/lib/webrick/httpresponse.rb b/lib/webrick/httpresponse.rb index 34c1018287..c120cae09c 100644 --- a/lib/webrick/httpresponse.rb +++ b/lib/webrick/httpresponse.rb @@ -310,6 +310,8 @@ module WEBrick def send_body(socket) # :nodoc: if @body.respond_to? :readpartial then send_body_io(socket) + elsif @body.respond_to?(:call) then + send_body_proc(socket) else send_body_string(socket) end @@ -428,9 +430,20 @@ module WEBrick end _write_data(socket, "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 + begin + @sent_size = IO.copy_stream(@body, socket, size, offset) + rescue NotImplementedError + @body.seek(offset, IO::SEEK_SET) + @sent_size = IO.copy_stream(@body, socket, size) + end end ensure @body.close @@ -459,24 +472,41 @@ module WEBrick end 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 + def send_body_proc(socket) + if @request_method == "HEAD" + # do nothing + elsif chunked? + @body.call(ChunkedWrapper.new(socket, self)) + _write_data(socket, "0#{CRLF}#{CRLF}") + else + size = @header['content-length'].to_i + @body.call(socket) + @sent_size = size + end + end + + class ChunkedWrapper + def initialize(socket, resp) + @socket = socket + @resp = resp 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 write(buf) + 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) + data.clear + @sent_size += size + size + } + end + + def <<(*buf) + write(buf) + self end end diff --git a/lib/webrick/httpservlet/filehandler.rb b/lib/webrick/httpservlet/filehandler.rb index 068246c9d0..7aa0d9fb56 100644 --- a/lib/webrick/httpservlet/filehandler.rb +++ b/lib/webrick/httpservlet/filehandler.rb @@ -87,6 +87,35 @@ 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}" + ) + + begin + IO.copy_stream(body, socket, last - first + 1, first) + rescue NotImplementedError + body.seek(first, IO::SEEK_SET) + IO.copy_stream(body, socket, last - first + 1) + end + 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']) @@ -97,37 +126,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 |