Class | Camping::FastCGI |
In: |
lib/camping/fastcgi.rb
|
Parent: | Object |
Camping::FastCGI is a small class for hooking one or more Camping apps up to FastCGI. Generally, you‘ll use this class in your application‘s postamble.
if __FILE__ == $0 require 'camping/fastcgi' Camping::FastCGI.start(YourApp) end
This example is stripped down to the basics. The postamble has no database connection. It just loads this class and calls Camping::FastCGI.start.
Now, in Lighttpd or Apache, you can point to your app‘s file, which will be executed, only to discover that your app now speaks the FastCGI protocol.
Here‘s a sample lighttpd.conf (tested with Lighttpd 1.4.11) to serve as example:
server.port = 3044 server.bind = "127.0.0.1" server.modules = ( "mod_fastcgi" ) server.document-root = "/var/www/camping/blog/" server.errorlog = "/var/www/camping/blog/error.log" #### fastcgi module fastcgi.server = ( "/" => ( "localhost" => ( "socket" => "/tmp/camping-blog.socket", "bin-path" => "/var/www/camping/blog/blog.rb", "check-local" => "disable", "max-procs" => 1 ) ) )
The file /var/www/camping/blog/blog.rb is the Camping app with the postamble.
require 'camping/fastcgi' fast = Camping::FastCGI.new fast.mount("/blog", Blog) fast.mount("/tepee", Tepee) fast.mount("/", Index) fast.start
CHUNK_SIZE | = | (4 * 1024) |
Creates a Camping::FastCGI class with empty mounts.
# File lib/camping/fastcgi.rb, line 62 62: def initialize 63: @mounts = {} 64: end
Serve an entire directory of Camping apps. (See code.whytheluckystiff.net/camping/wiki/TheCampingServer.)
Use this method inside your FastCGI dispatcher:
#!/usr/local/bin/ruby require 'rubygems' require 'camping/fastcgi' Camping::Models::Base.establish_connection :adapter => 'sqlite3', :database => "/path/to/db" Camping::FastCGI.serve("/home/why/cvs/camping/examples")
# File lib/camping/fastcgi.rb, line 161 161: def self.serve(path, index=nil) 162: require 'camping/reloader' 163: if File.directory? path 164: fast = Camping::FastCGI.new 165: script_load = proc do |script| 166: app = Camping::Reloader.new(script) 167: fast.mount("/#{app.mount}", app) 168: app 169: end 170: Dir[File.join(path, '*.rb')].each &script_load 171: fast.mount("/", index) if index 172: 173: fast.start do |dir, app| 174: Dir[File.join(path, dir, '*.rb')].each do |script| 175: smount = "/" + File.basename(script, '.rb') 176: script_load[script] unless @mounts.has_key? smount 177: end 178: end 179: else 180: start(Camping::Reloader.new(path)) 181: end 182: end
A simple single-app starter mechanism
Camping::FastCGI.start(Blog)
# File lib/camping/fastcgi.rb, line 144 144: def self.start(app) 145: cf = Camping::FastCGI.new 146: cf.mount("/", app) 147: cf.start 148: end
Starts the FastCGI main loop.
# File lib/camping/fastcgi.rb, line 74 74: def start 75: FCGI.each do |req| 76: dir, app = nil 77: begin 78: root, path = "/" 79: if ENV['FORCE_ROOT'] and ENV['FORCE_ROOT'].to_i == 1 80: path = req.env['REQUEST_URI'] 81: else 82: root = req.env['SCRIPT_NAME'] 83: path = req.env['PATH_INFO'] 84: end 85: 86: dir, app = @mounts.max { |a,b| match(path, a[0]) <=> match(path, b[0]) } 87: unless dir and app 88: dir, app = '/', Camping 89: end 90: yield dir, app if block_given? 91: 92: req.env['SERVER_SCRIPT_NAME'] = req.env['SCRIPT_NAME'] 93: req.env['SERVER_PATH_INFO'] = req.env['PATH_INFO'] 94: req.env['SCRIPT_NAME'] = File.join(root, dir) 95: req.env['PATH_INFO'] = path.gsub(/^#{dir}/, '') 96: 97: controller = app.run(req.in, req.env) 98: sendfile = nil 99: headers = {} 100: controller.headers.each do |k, v| 101: if k =~ /^X-SENDFILE$/i and !ENV['SERVER_X_SENDFILE'] 102: sendfile = v 103: else 104: headers[k] = v 105: end 106: end 107: 108: body = controller.body 109: controller.body = "" 110: controller.headers = headers 111: 112: req.out << controller.to_s 113: if sendfile 114: File.open(sendfile, "rb") do |f| 115: while chunk = f.read(CHUNK_SIZE) and chunk.length > 0 116: req.out << chunk 117: end 118: end 119: elsif body.respond_to? :read 120: while chunk = body.read(CHUNK_SIZE) and chunk.length > 0 121: req.out << chunk 122: end 123: body.close if body.respond_to? :close 124: else 125: req.out << body.to_s 126: end 127: rescue Exception => e 128: req.out << "Content-Type: text/html\r\n\r\n" + 129: "<h1>Camping Problem!</h1>" + 130: "<h2><strong>#{root}</strong>#{path}</h2>" + 131: "<h3>#{e.class} #{esc e.message}</h3>" + 132: "<ul>" + e.backtrace.map { |bt| "<li>#{esc bt}</li>" }.join + "</ul>" + 133: "<hr /><p>#{req.env.inspect}</p>" 134: ensure 135: req.finish 136: end 137: end 138: end