mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Added send_file method to the HttpResponse, culling parts from the DirHandler. Added support for the X-SENDFILE header to the Camping bridge.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@155 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
894f4a0bd3
commit
3cf03ae9f3
4 changed files with 62 additions and 27 deletions
|
@ -9,6 +9,15 @@ require 'mongrel/tcphack'
|
|||
require 'yaml'
|
||||
require 'time'
|
||||
|
||||
begin
|
||||
require 'rubygems'
|
||||
require 'sendfile'
|
||||
$mongrel_has_sendfile = true
|
||||
STDERR.puts "** You have sendfile installed, will use that to serve files."
|
||||
rescue Object
|
||||
$mongrel_has_sendfile = false
|
||||
end
|
||||
|
||||
# Mongrel module containing all of the classes (include C extensions) for running
|
||||
# a Mongrel web server. It contains a minimalist HTTP server with just enough
|
||||
# functionality to service web application requests fast as possible.
|
||||
|
@ -324,6 +333,25 @@ module Mongrel
|
|||
end
|
||||
end
|
||||
|
||||
# Appends the contents of +path+ to the response stream. The file is opened for binary
|
||||
# reading and written in chunks to the socket. If the
|
||||
# <a href="http://rubyforge.org/projects/ruby-sendfile">sendfile</a> library is found,
|
||||
# it is used to send the file, often with greater speed and less memory/cpu usage.
|
||||
def send_file(path)
|
||||
File.open(path, "rb") do |f|
|
||||
if @socket.respond_to? :sendfile
|
||||
@socket.sendfile(f)
|
||||
else
|
||||
while chunk = f.read(Const::CHUNK_SIZE)
|
||||
@socket.write(chunk)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
||||
# ignore these since it means the client closed off early
|
||||
STDERR.puts "Client closed socket requesting file #{req}: #$!"
|
||||
end
|
||||
|
||||
def write(data)
|
||||
@socket.write(data)
|
||||
end
|
||||
|
|
|
@ -34,21 +34,30 @@ module Mongrel
|
|||
def process(request, response)
|
||||
req = StringIO.new(request.body)
|
||||
controller = @klass.run(req, request.params)
|
||||
sendfile = nil
|
||||
response.start(controller.status) do |head,out|
|
||||
controller.headers.each do |k, v|
|
||||
[*v].each do |vi|
|
||||
head[k] = vi
|
||||
if k =~ /^X-SENDFILE$/i
|
||||
sendfile = v
|
||||
else
|
||||
[*v].each do |vi|
|
||||
head[k] = vi
|
||||
end
|
||||
end
|
||||
end
|
||||
if controller.body.respond_to? :read
|
||||
response.send_header
|
||||
|
||||
if sendfile
|
||||
response.send_file(sendfile)
|
||||
elsif controller.body.respond_to? :read
|
||||
while chunk = controller.body.read(16384)
|
||||
out << chunk
|
||||
@response.write(chunk)
|
||||
end
|
||||
if controller.body.respond_to? :close
|
||||
controller.body.close
|
||||
end
|
||||
else
|
||||
out << controller.body
|
||||
@response.write(controller.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,3 @@
|
|||
require 'rubygems'
|
||||
begin
|
||||
require 'sendfile'
|
||||
$mongrel_has_sendfile = true
|
||||
STDERR.puts "** You have sendfile installed, will use that to serve files."
|
||||
rescue Object
|
||||
$mongrel_has_sendfile = false
|
||||
end
|
||||
|
||||
module Mongrel
|
||||
|
||||
# You implement your application handler with this. It's very light giving
|
||||
|
@ -188,22 +179,10 @@ module Mongrel
|
|||
response.send_header
|
||||
|
||||
if not header_only
|
||||
begin
|
||||
if $mongrel_has_sendfile
|
||||
File.open(req, "rb") { |f| response.socket.sendfile(f) }
|
||||
else
|
||||
File.open(req, "rb") { |f| response.socket.write(f.read) }
|
||||
end
|
||||
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
||||
# ignore these since it means the client closed off early
|
||||
STDERR.puts "Client closed socket requesting file #{req}: #$!"
|
||||
end
|
||||
else
|
||||
response.send_body # should send nothing
|
||||
response.send_file(req)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Process the request to either serve a file or a directory listing
|
||||
# if allowed (based on the listing_allowed paramter to the constructor).
|
||||
def process(request, response)
|
||||
|
|
|
@ -44,6 +44,25 @@ class ResponseTest < Test::Unit::TestCase
|
|||
assert io.length > 0, "output didn't have data"
|
||||
end
|
||||
|
||||
def test_response_file
|
||||
contents = "PLAIN TEXT\r\nCONTENTS\r\n"
|
||||
require 'tempfile'
|
||||
tmpf = Tempfile.new("test_response_file")
|
||||
tmpf.write(contents)
|
||||
tmpf.rewind
|
||||
|
||||
io = StringIO.new
|
||||
resp = HttpResponse.new(io)
|
||||
resp.start(200) do |head,out|
|
||||
head['Content-Type'] = 'text/plain'
|
||||
resp.send_header
|
||||
resp.send_file(tmpf.path)
|
||||
end
|
||||
io.rewind
|
||||
tmpf.close
|
||||
|
||||
assert io.length > 0, "output didn't have data"
|
||||
assert io.read =~ /#{contents}\Z/, "output doesn't end with file payload"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue