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