mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Final tweaks to speed up the file serving a bit using sendfile and a modified file handler.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@124 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
4d9966ee00
commit
110e92752b
4 changed files with 253 additions and 216 deletions
|
@ -4,6 +4,7 @@
|
|||
# This is where Win32::Daemon resides.
|
||||
###############################################
|
||||
require 'rubygems'
|
||||
require 'mongrel'
|
||||
require 'mongrel/rails'
|
||||
require 'optparse'
|
||||
require 'win32/service'
|
||||
|
@ -78,6 +79,7 @@ class MongrelRails
|
|||
@server.register("/", @rails)
|
||||
|
||||
dbg "delayed_initialize left"
|
||||
|
||||
end
|
||||
|
||||
def load_mime_map
|
||||
|
@ -104,11 +106,12 @@ class MongrelRails
|
|||
|
||||
Dir.chdir(@rails_root)
|
||||
|
||||
|
||||
ENV['RAILS_ENV'] = @environment
|
||||
require File.join(@rails_root, 'config/environment')
|
||||
require 'config/environment'
|
||||
|
||||
# configure the rails handler
|
||||
rails = RailsHandler.new(@docroot, load_mime_map)
|
||||
rails = Mongrel::Rails::RailsHandler.new(@docroot, load_mime_map)
|
||||
|
||||
dbg "configure_rails left"
|
||||
|
||||
|
@ -116,6 +119,7 @@ class MongrelRails
|
|||
end
|
||||
|
||||
def start_serve
|
||||
begin
|
||||
dbg "start_serve entered"
|
||||
|
||||
@runner = Thread.new do
|
||||
|
@ -134,6 +138,10 @@ class MongrelRails
|
|||
@runner.run
|
||||
|
||||
dbg "start_serve left"
|
||||
rescue
|
||||
dbg "ERROR: #$!\r\n"
|
||||
dbg $!.backtrace.join("\r\n")
|
||||
end
|
||||
end
|
||||
|
||||
def stop_serve
|
||||
|
@ -193,6 +201,7 @@ class RailsDaemon < Win32::Daemon
|
|||
end
|
||||
|
||||
|
||||
begin
|
||||
if ARGV[0] == 'service'
|
||||
ARGV.shift
|
||||
|
||||
|
@ -280,12 +289,19 @@ elsif ARGV[0] == 'debug'
|
|||
begin
|
||||
sleep
|
||||
rescue Interrupt
|
||||
dbg "ERROR: #$!\r\n"
|
||||
dbg $!.backtrace.join("\r\n")
|
||||
puts "graceful shutdown?"
|
||||
end
|
||||
|
||||
begin
|
||||
rails.stop_serve
|
||||
rescue
|
||||
dbg "ERROR: #$!\r\n"
|
||||
dbg $!.backtrace.join("\r\n")
|
||||
end
|
||||
|
||||
end
|
||||
rescue
|
||||
dbg "ERROR: #$!\r\n"
|
||||
dbg $!.backtrace.join("\r\n")
|
||||
end
|
||||
|
|
|
@ -200,6 +200,7 @@ module Mongrel
|
|||
@out.write(value)
|
||||
@out.write("\r\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Writes and controls your response to the client using the HTTP/1.1 specification.
|
||||
|
@ -306,6 +307,10 @@ module Mongrel
|
|||
end
|
||||
end
|
||||
|
||||
def write(data)
|
||||
@socket.write(data)
|
||||
end
|
||||
|
||||
# This takes whatever has been done to header and body and then writes it in the
|
||||
# proper format to make an HTTP/1.1 response.
|
||||
def finished
|
||||
|
@ -317,6 +322,7 @@ module Mongrel
|
|||
def done
|
||||
(@status_sent and @header_sent and @body_sent)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
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
|
||||
|
||||
|
@ -150,19 +158,28 @@ module Mongrel
|
|||
# Sends the contents of a file back to the user. Not terribly efficient since it's
|
||||
# opening and closing the file for each read.
|
||||
def send_file(req, response)
|
||||
response.start(200) do |head,out|
|
||||
|
||||
# first we setup the headers and status then we do a very fast send on the socket directly
|
||||
response.status = 200
|
||||
|
||||
# set the mime type from our map based on the ending
|
||||
dot_at = req.rindex(".")
|
||||
if dot_at
|
||||
ext = req[dot_at .. -1]
|
||||
if MIME_TYPES[ext]
|
||||
head['Content-Type'] = MIME_TYPES[ext]
|
||||
response.header['Content-Type'] = MIME_TYPES[ext]
|
||||
end
|
||||
end
|
||||
|
||||
open(req, "rb") do |f|
|
||||
out.write(f.read)
|
||||
end
|
||||
response.header['Content-Length'] = File.size(req)
|
||||
|
||||
response.send_status
|
||||
response.send_header
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -184,11 +201,8 @@ module Mongrel
|
|||
send_file(req, response)
|
||||
end
|
||||
rescue => details
|
||||
response.reset
|
||||
response.start(403) do |head,out|
|
||||
out << "Error accessing file: #{details}"
|
||||
out << details.backtrace.join("\n")
|
||||
end
|
||||
STDERR.puts "Error accessing file: #{details}"
|
||||
STDERR.puts details.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,6 +4,92 @@ require 'cgi'
|
|||
module Mongrel
|
||||
module Rails
|
||||
|
||||
|
||||
# Implements a handler that can run Rails and serve files out of the
|
||||
# Rails application's public directory. This lets you run your Rails
|
||||
# application with Mongrel during development and testing, then use it
|
||||
# also in production behind a server that's better at serving the
|
||||
# static files.
|
||||
#
|
||||
# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
|
||||
# mapping that it should add to the list of valid mime types.
|
||||
#
|
||||
# It also supports page caching directly and will try to resolve a request
|
||||
# in the following order:
|
||||
#
|
||||
# * If the requested exact PATH_INFO exists as a file then serve it.
|
||||
# * If it exists at PATH_INFO+".html" exists then serve that.
|
||||
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
|
||||
#
|
||||
# This means that if you are using page caching it will actually work with Mongrel
|
||||
# and you should see a decent speed boost (but not as fast as if you use lighttpd).
|
||||
#
|
||||
# An additional feature you can use is
|
||||
class RailsHandler < Mongrel::HttpHandler
|
||||
attr_reader :files
|
||||
attr_reader :guard
|
||||
|
||||
def initialize(dir, mime_map = {})
|
||||
@files = Mongrel::DirHandler.new(dir,false)
|
||||
@guard = Mutex.new
|
||||
|
||||
# register the requested mime types
|
||||
mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
|
||||
end
|
||||
|
||||
# Attempts to resolve the request as follows:
|
||||
#
|
||||
#
|
||||
# * If the requested exact PATH_INFO exists as a file then serve it.
|
||||
# * If it exists at PATH_INFO+".html" exists then serve that.
|
||||
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
|
||||
def process(request, response)
|
||||
return if response.socket.closed?
|
||||
|
||||
path_info = request.params[Mongrel::Const::PATH_INFO]
|
||||
page_cached = request.params[Mongrel::Const::PATH_INFO] + ".html"
|
||||
|
||||
if @files.can_serve(path_info)
|
||||
# File exists as-is so serve it up
|
||||
@files.process(request,response)
|
||||
elsif @files.can_serve(page_cached)
|
||||
# possible cached page, serve it up
|
||||
request.params[Mongrel::Const::PATH_INFO] = page_cached
|
||||
@files.process(request,response)
|
||||
else
|
||||
begin
|
||||
cgi = Mongrel::CGIWrapper.new(request, response)
|
||||
cgi.handler = self
|
||||
|
||||
@guard.synchronize do
|
||||
# Rails is not thread safe so must be run entirely within synchronize
|
||||
Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
|
||||
end
|
||||
|
||||
# This finalizes the output using the proper HttpResponse way
|
||||
cgi.out {""}
|
||||
rescue Errno::EPIPE
|
||||
# ignored
|
||||
rescue Object => rails_error
|
||||
STDERR.puts "Error calling Dispatcher.dispatch #{rails_error.inspect}"
|
||||
STDERR.puts rails_error.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Does the internal reload for Rails. It might work for most cases, but
|
||||
# sometimes you get exceptions. In that case just do a real restart.
|
||||
def reload!
|
||||
@guard.synchronize do
|
||||
$".replace $orig_dollar_quote
|
||||
GC.start
|
||||
Dispatcher.reset_application!
|
||||
ActionController::Routing::Routes.reload
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Creates Rails specific configuration options for people to use
|
||||
# instead of the base Configurator.
|
||||
class RailsConfigurator < Mongrel::Configurator
|
||||
|
@ -86,91 +172,6 @@ module Mongrel
|
|||
log "WARNING: Rails does not support signals on Win32."
|
||||
end
|
||||
end
|
||||
|
||||
# Implements a handler that can run Rails and serve files out of the
|
||||
# Rails application's public directory. This lets you run your Rails
|
||||
# application with Mongrel during development and testing, then use it
|
||||
# also in production behind a server that's better at serving the
|
||||
# static files.
|
||||
#
|
||||
# The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype
|
||||
# mapping that it should add to the list of valid mime types.
|
||||
#
|
||||
# It also supports page caching directly and will try to resolve a request
|
||||
# in the following order:
|
||||
#
|
||||
# * If the requested exact PATH_INFO exists as a file then serve it.
|
||||
# * If it exists at PATH_INFO+".html" exists then serve that.
|
||||
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
|
||||
#
|
||||
# This means that if you are using page caching it will actually work with Mongrel
|
||||
# and you should see a decent speed boost (but not as fast as if you use lighttpd).
|
||||
#
|
||||
# An additional feature you can use is
|
||||
class RailsHandler < Mongrel::HttpHandler
|
||||
attr_reader :files
|
||||
attr_reader :guard
|
||||
|
||||
def initialize(dir, mime_map = {})
|
||||
@files = Mongrel::DirHandler.new(dir,false)
|
||||
@guard = Mutex.new
|
||||
|
||||
# register the requested mime types
|
||||
mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) }
|
||||
end
|
||||
|
||||
# Attempts to resolve the request as follows:
|
||||
#
|
||||
#
|
||||
# * If the requested exact PATH_INFO exists as a file then serve it.
|
||||
# * If it exists at PATH_INFO+".html" exists then serve that.
|
||||
# * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispath to have Rails go.
|
||||
def process(request, response)
|
||||
return if response.socket.closed?
|
||||
|
||||
path_info = request.params[Mongrel::Const::PATH_INFO]
|
||||
page_cached = request.params[Mongrel::Const::PATH_INFO] + ".html"
|
||||
|
||||
if @files.can_serve(path_info)
|
||||
# File exists as-is so serve it up
|
||||
@files.process(request,response)
|
||||
elsif @files.can_serve(page_cached)
|
||||
# possible cached page, serve it up
|
||||
request.params[Mongrel::Const::PATH_INFO] = page_cached
|
||||
@files.process(request,response)
|
||||
else
|
||||
begin
|
||||
cgi = Mongrel::CGIWrapper.new(request, response)
|
||||
cgi.handler = self
|
||||
|
||||
@guard.synchronize do
|
||||
# Rails is not thread safe so must be run entirely within synchronize
|
||||
Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body)
|
||||
end
|
||||
|
||||
# This finalizes the output using the proper HttpResponse way
|
||||
cgi.out {""}
|
||||
rescue Errno::EPIPE
|
||||
# ignored
|
||||
rescue Object => rails_error
|
||||
log "Error calling Dispatcher.dispatch #{rails_error.inspect}"
|
||||
log rails_error.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Does the internal reload for Rails. It might work for most cases, but
|
||||
# sometimes you get exceptions. In that case just do a real restart.
|
||||
def reload!
|
||||
@guard.synchronize do
|
||||
$".replace $orig_dollar_quote
|
||||
GC.start
|
||||
Dispatcher.reset_application!
|
||||
ActionController::Routing::Routes.reload
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue