Skip to content

Commit

Permalink
* lib/camping/fastcgi.rb: enclose the activity of the FCGI main loop…
Browse files Browse the repository at this point in the history
… into the `camp_do` method to keep memory from leaking.
  • Loading branch information
_why committed Oct 22, 2006
1 parent ad7867f commit 50023ad
Showing 1 changed file with 64 additions and 88 deletions.
152 changes: 64 additions & 88 deletions lib/camping/fastcgi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,71 +69,12 @@ def mount(dir, app)
dir.gsub!(/\/+$/, '')
@mounts[dir] = app
end

#
# Starts the FastCGI main loop.
def start
def start(&blk)
FCGI.each do |req|
dir, app = nil
begin
root, path = "/"
if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1
path = req.env['SCRIPT_NAME']
else
root = req.env['SCRIPT_NAME']
path = req.env['PATH_INFO']
end

dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) }
unless dir and app
dir, app = '/', Camping
end
yield dir, app if block_given?

req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME']
req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO']
req.env['SCRIPT_NAME'] = File.join(root, dir)
req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '')

controller = app.run(SeekStream.new(req.in), req.env)
sendfile = nil
headers = {}
controller.headers.each do |k, v|
if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE']
sendfile = v
else
headers[k] = v
end
end

body = controller.body
controller.body = ""
controller.headers = headers

req.out << controller.to_s
if sendfile
File.open(sendfile, "rb") do |f|
while chunk = f.read(CHUNK_SIZE) and chunk.length > 0
req.out << chunk
end
end
elsif body.respond_to? :read
while chunk = body.read(CHUNK_SIZE) and chunk.length > 0
req.out << chunk
end
body.close if body.respond_to? :close
else
req.out << body.to_s
end
rescue Exception => e
req.out << "Content-Type: text/html\r\n\r\n" +
"<h1>Camping Problem!</h1>" +
"<h2><strong>#{root}</strong>#{path}</h2>" +
"<h3>#{e.class} #{esc e.message}</h3>" +
"<ul>" + e.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" +
"<hr /><p>#{req.env.inspect}</p>"
ensure
req.finish
end
camp_do(req, &blk)
end
end

Expand Down Expand Up @@ -183,6 +124,67 @@ def self.serve(path, index=nil)

private

def camp_do(req)
root, path, dir, app = "/"
if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1
path = req.env['SCRIPT_NAME']
else
root = req.env['SCRIPT_NAME']
path = req.env['PATH_INFO']
end

dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) }
unless dir and app
dir, app = '/', Camping
end
yield dir, app if block_given?

req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME']
req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO']
req.env['SCRIPT_NAME'] = File.join(root, dir)
req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '')

controller = app.run(SeekStream.new(req.in), req.env)
sendfile = nil
headers = {}
controller.headers.each do |k, v|
if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE']
sendfile = v
else
headers[k] = v
end
end

body = controller.body
controller.body = ""
controller.headers = headers

req.out << controller.to_s
if sendfile
File.open(sendfile, "rb") do |f|
while chunk = f.read(CHUNK_SIZE) and chunk.length > 0
req.out << chunk
end
end
elsif body.respond_to? :read
while chunk = body.read(CHUNK_SIZE) and chunk.length > 0
req.out << chunk
end
body.close if body.respond_to? :close
else
req.out << body.to_s
end
rescue Exception => e
req.out << "Content-Type: text/html\r\n\r\n" +
"<h1>Camping Problem!</h1>" +
"<h2><strong>#{root}</strong>#{path}</h2>" +
"<h3>#{e.class} #{esc e.message}</h3>" +
"<ul>" + e.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" +
"<hr /><p>#{req.env.inspect}</p>"
ensure
req.finish
end

def match(path, mount)
m = path.match(/^#{Regexp::quote mount}(\/|$)/)
if m; m.end(0)
Expand All @@ -194,32 +196,6 @@ def esc(str)
str.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
end

class SeekStream2
def initialize(stream)
@last_read = ""
@stream = stream
end
def eof?
@stream.eof?
end
def each
while( line = @stream.gets )
yield line
end
end
def read(len = 16384)
@last_read = @stream.read(len)
end
def seek(len, typ)
raise NotImplementedError, "only IO::SEEK_CUR is supported with SeekStream" if typ != IO::SEEK_CUR
raise NotImplementedError, "only rewinding is supported with SeekStream" if len > 0
raise NotImplementedError, "rewinding #{-len} past the buffer #{@last_read.size} start not supported with SeekStream" if -len > @last_read.size
-1.downto(len) { |x| @stream.ungetc(@last_read[x]) }
@last_read = ""
self
end
end

class SeekStream
def initialize(stream)
@last_read = ""
Expand Down

0 comments on commit 50023ad

Please sign in to comment.