1
0
Fork 0
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:
why 2006-04-11 00:00:52 +00:00
parent 894f4a0bd3
commit 3cf03ae9f3
4 changed files with 62 additions and 27 deletions

View file

@ -9,6 +9,15 @@ require 'mongrel/tcphack'
require 'yaml' require 'yaml'
require 'time' 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 # 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 # a Mongrel web server. It contains a minimalist HTTP server with just enough
# functionality to service web application requests fast as possible. # functionality to service web application requests fast as possible.
@ -324,6 +333,25 @@ module Mongrel
end end
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) def write(data)
@socket.write(data) @socket.write(data)
end end

View file

@ -34,21 +34,30 @@ module Mongrel
def process(request, response) def process(request, response)
req = StringIO.new(request.body) req = StringIO.new(request.body)
controller = @klass.run(req, request.params) controller = @klass.run(req, request.params)
sendfile = nil
response.start(controller.status) do |head,out| response.start(controller.status) do |head,out|
controller.headers.each do |k, v| controller.headers.each do |k, v|
[*v].each do |vi| if k =~ /^X-SENDFILE$/i
head[k] = vi sendfile = v
else
[*v].each do |vi|
head[k] = vi
end
end 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) while chunk = controller.body.read(16384)
out << chunk @response.write(chunk)
end end
if controller.body.respond_to? :close if controller.body.respond_to? :close
controller.body.close controller.body.close
end end
else else
out << controller.body @response.write(controller.body)
end end
end end
end end

View file

@ -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 module Mongrel
# You implement your application handler with this. It's very light giving # You implement your application handler with this. It's very light giving
@ -188,22 +179,10 @@ module Mongrel
response.send_header response.send_header
if not header_only if not header_only
begin response.send_file(req)
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
end end
end end
# Process the request to either serve a file or a directory listing # Process the request to either serve a file or a directory listing
# if allowed (based on the listing_allowed paramter to the constructor). # if allowed (based on the listing_allowed paramter to the constructor).
def process(request, response) def process(request, response)

View file

@ -44,6 +44,25 @@ class ResponseTest < Test::Unit::TestCase
assert io.length > 0, "output didn't have data" assert io.length > 0, "output didn't have data"
end 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 end