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 '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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue