
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

Creates a Camping::FastCGI class with empty mounts.
[ show source ]
# 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")
[ show source ]
# 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)
[ show source ]
# 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

Mounts a Camping application. The dir being the name of the directory to serve as the application‘s root. The app is a Camping class.
[ show source ]
# File lib/camping/fastcgi.rb, line 67
67: def mount(dir, app)
68: dir.gsub!(/\/{2,}/, '/')
69: dir.gsub!(/\/+$/, '')
70: @mounts[dir] = app
71: end

Starts the FastCGI main loop.
[ show source ]
# 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