lib/merb/merb_handler.rb in merb-0.0.2 vs lib/merb/merb_handler.rb in merb-0.0.3
- old
+ new
@@ -1,66 +1,111 @@
class MerbHandler < Mongrel::HttpHandler
+ @@file_only_methods = ["GET","HEAD"]
+ # take the name of a directory and use that as the doc root or public
+ # directory of your site. This is set to the root of your merb app + '/public'
+ # by default. See merb_daemon.rb if you want to change this.
+ def initialize(dir, opts = {})
+ @files = Mongrel::DirHandler.new(dir,false)
+ end
+
+ # process incoming http requests and do a number of things
+ # 1. check for rails style cached pages. add .html to the
+ # url and see if there is a static file in public that matches.
+ # serve that file directly without invoking Merb and be done with it.
+ # 2. Serve static asset and html files directly from public/ if
+ # they exist and fall back to Merb otherwise
+ # 3. If none of the above apply, we take apart the request url
+ # and feed it into Merb::RouteMatcher to let it decide which
+ # controller and method will serve the request.
+ # 4. after the controller has done its thing, we check for the
+ # X-SENDFILE header. if you set this header to the path to a file
+ # in your controller then mongrel will serve the file directly
+ # and your controller can go on processing other requests.
def process(request, response)
+
if response.socket.closed?
return
end
- begin
- controller, method, args = handle(request)
- output = if (controller && controller.kind_of?(Merb::Controller))
- if method
- ( args ? controller.send( method, args ) : controller.send(method) )
+ # Rails style page caching. Check the public dir first for
+ # .html pages and serve directly. Otherwise fall back to Merb
+ # routing and request dispatching.
+ path_info = request.params[Mongrel::Const::PATH_INFO]
+ page_cached = path_info + ".html"
+ get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
+
+ if get_or_head and @files.can_serve(path_info)
+ # File exists as-is so serve it up
+ @files.process(request,response)
+ elsif get_or_head and @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
+ controller, method, args = handle(request)
+ output = if (controller && controller.kind_of?(Merb::Controller))
+ if method
+ ( args ? controller.send( method, args ) : controller.send(method) )
+ else
+ controller.to_s
+ end
else
- controller.to_s
+ nil
+ end
+ rescue Exception => e
+ response.start(500) do |head,out|
+ head["Content-Type"] = "text/html"
+ out <<"<html><h2>Merb Error!</h2><pre>#{ e.message } - (#{ e.class })" <<
+ "\n" << "#{(e.backtrace or []).join('\n')}</pre></html>"
end
- else
- nil
- end
- rescue Exception => e
- response.start(500) do |head,out|
- head["Content-Type"] = "text/html"
- out <<"<html><h2>Merb Error!</h2><pre>#{ e.message } - (#{ e.class })" <<
- "\n" << "#{(e.backtrace or []).join('\n')}</pre></html>"
+ return
end
- return
- end
-
- unless output
- response.start(404) do |head,out|
- head["Content-Type"] = "text/html"
- out << "<html><body>Error: no merb controller found for this url.</body></html>"
+
+ unless output
+ response.start(404) do |head,out|
+ head["Content-Type"] = "text/html"
+ out << "<html><body>Error: no merb controller found for this url.</body></html>"
+ end
+ return
+ end
+
+ sendfile, clength = nil
+ response.status = controller.status
+
+ # check for the X-SENDFILE heaqder from your Merb::Controller
+ # and serve the file directly instead of buffering.
+ controller.headers.each do |k, v|
+ if k =~ /^X-SENDFILE$/i
+ sendfile = v
+ elsif k =~ /^CONTENT-LENGTH$/i
+ clength = v.to_i
+ else
+ [*v].each do |vi|
+ response.header[k] = vi
+ end
+ end
end
- return
- end
-
- sendfile, clength = nil
- response.status = controller.status
- controller.headers.each do |k, v|
- if k =~ /^X-SENDFILE$/i
- sendfile = v
- elsif k =~ /^CONTENT-LENGTH$/i
- clength = v.to_i
+
+ if sendfile
+ # send X-SENDFILE header to mongrel
+ response.send_status(File.size(sendfile))
+ response.send_header
+ response.send_file(sendfile)
else
- [*v].each do |vi|
- response.header[k] = vi
- end
+ # render response from successful controller
+ response.send_status(output.length)
+ response.send_header
+ response.write(output)
end
end
-
- if sendfile
- response.send_status(File.size(sendfile))
- response.send_header
- response.send_file(sendfile)
- else
- response.send_status(output.length)
- response.send_header
- response.write(output)
- end
-
end
+ # This is where we grab the incoming request PATH_INFO
+ # and use that in the merb routematcher to determine
+ # which controller and method to run.
def handle(request)
path = request.params["PATH_INFO"].sub(/\/+/, '/')
path = path[0..-2] if (path[-1] == ?/)
routes = Merb::RouteMatcher.new
route = routes.route_request(path)
@@ -70,9 +115,13 @@
else
["<html><body>Error: no route matches!</body></html>", nil, nil]
end
end
+ # take a controller class name string and reload or require
+ # the right controller file then upcase the first letter and
+ # trun it into a new object p[assing in the request and response.
+ # this is where your Merb::Controller is instantiated.
def instantiate_controller(controller_name, req, env)
if !File.exist?(Merb::Server.config[:merb_root]+"/app/controllers/#{controller_name}.rb")
return Object.const_get(:Noroutefound).new(req, env)
end
controller_name.import
\ No newline at end of file