diff options
-rw-r--r-- | bin/mongrel_rails | 21 | ||||
-rw-r--r-- | ext/http11/http11.c | 22 | ||||
-rw-r--r-- | ext/http11/tst_search.c | 5 | ||||
-rw-r--r-- | lib/mongrel.rb | 48 | ||||
-rw-r--r-- | test/test_uriclassifier.rb | 23 |
5 files changed, 92 insertions, 27 deletions
diff --git a/bin/mongrel_rails b/bin/mongrel_rails index 3f7b124..0392949 100644 --- a/bin/mongrel_rails +++ b/bin/mongrel_rails @@ -1,4 +1,4 @@ -require 'config/environment' +require 'rubygems' require 'mongrel' require 'cgi' begin @@ -78,12 +78,13 @@ end cwd = Dir.pwd -#Daemonize.daemonize(log_file=File.join(cwd,"log","mongrel.log")) -#Dir.chdir(cwd) - -open(File.join(cwd,"log/mongrel-#{ARGV[1]}.pid"),"w") {|f| f.write(Process.pid) } -h = Mongrel::HttpServer.new(ARGV[0], ARGV[1]) -h.register("/", RailsHandler.new(File.join(cwd,"public"))) -h.run - -h.acceptor.join +Daemonize.daemonize(log_file=File.join(cwd,"log","mongrel.log")) +Dir.chdir(cwd) do + require 'config/environment' + open(File.join(cwd,"log/mongrel-#{ARGV[1]}.pid"),"w") {|f| f.write(Process.pid) } + h = Mongrel::HttpServer.new(ARGV[0], ARGV[1]) + h.register("/", RailsHandler.new(File.join(cwd,"public"))) + h.run + + h.acceptor.join +end diff --git a/ext/http11/http11.c b/ext/http11/http11.c index c2659c4..5dc5e09 100644 --- a/ext/http11/http11.c +++ b/ext/http11/http11.c @@ -341,7 +341,9 @@ VALUE URIClassifier_unregister(VALUE self, VALUE uri) * uc.resolve("/someuri") -> "/someuri", "", handler * uc.resolve("/someuri/pathinfo") -> "/someuri", "/pathinfo", handler * uc.resolve("/notfound/orhere") -> nil, nil, nil - * + * uc.resolve("/") -> "/", "/", handler # if uc.register("/", handler) + * uc.resolve("/path/from/root") -> "/", "/path/from/root", handler # if uc.register("/", handler) + * * Attempts to resolve either the whole URI or at the longest prefix, returning * the prefix (as script_info), path (as path_info), and registered handler * (usually an HttpHandler). If it doesn't find a handler registered at the longest @@ -358,7 +360,13 @@ VALUE URIClassifier_unregister(VALUE self, VALUE uri) * It also means that it's very efficient to do this only taking as long as the URI has * characters. * - * It expects strings with no embedded '\0' characters. Don't try other string-line stuff yet. + * A slight modification to the CGI 1.2 standard is given for handlers registered to "/". + * CGI expects all CGI scripts to be at some script path, so it doesn't really say anything + * about a script that handles the root. To make this work, the resolver will detect that + * the requested handler is at "/", and return that for script_name, and then simply return + * the full URI back as path_info. + * + * It expects strings with no embedded '\0' characters. Don't try other string-like stuff yet. */ VALUE URIClassifier_resolve(VALUE self, VALUE uri) { @@ -379,7 +387,15 @@ VALUE URIClassifier_resolve(VALUE self, VALUE uri) if(handler) { rb_ary_push(result, rb_str_substr (uri, 0, pref_len)); - rb_ary_push(result, rb_str_substr(uri, pref_len, RSTRING(uri)->len)); + // compensate for a script_name="/" where we need to add the "/" to path_info to keep it consistent + if(pref_len == 1 && uri_str[0] == '/') { + // matches the root URI so we have to use the whole URI as the path_info + rb_ary_push(result, uri); + } else { + // matches a script so process like normal + rb_ary_push(result, rb_str_substr(uri, pref_len, RSTRING(uri)->len)); + } + rb_ary_push(result, (VALUE)handler); } else { // not found so push back nothing diff --git a/ext/http11/tst_search.c b/ext/http11/tst_search.c index 4cfe6ff..44aee7a 100644 --- a/ext/http11/tst_search.c +++ b/ext/http11/tst_search.c @@ -32,7 +32,6 @@ void *tst_search(unsigned char *key, struct tst *tst, int *prefix_len) if(prefix_len) *prefix_len = key_index; return current_node->middle; } else { - current_node = current_node->middle; if(current_node && current_node->value == 0) { if(prefix_len) *prefix_len = key_index+1; @@ -47,7 +46,7 @@ void *tst_search(unsigned char *key, struct tst *tst, int *prefix_len) ((current_node->value != 0) && (key[key_index] < current_node->value)) ) { - if(current_node->left && current_node->value == 0) { + if(current_node->value == 0) { if(prefix_len) *prefix_len = key_index; longest_match = current_node->middle; } @@ -56,7 +55,7 @@ void *tst_search(unsigned char *key, struct tst *tst, int *prefix_len) } else { - if(current_node->right && current_node->value == 0) { + if(current_node->value == 0) { if(prefix_len) *prefix_len = key_index; longest_match = current_node->middle; } diff --git a/lib/mongrel.rb b/lib/mongrel.rb index d8e1b23..cac8d71 100644 --- a/lib/mongrel.rb +++ b/lib/mongrel.rb @@ -51,6 +51,8 @@ module Mongrel 505 => 'HTTP Version not supported' } + + # Frequently used constants when constructing requests or responses. Many times # the constant just refers to a string with the same contents. Using these constants # gave about a 3% to 10% performance improvement over using the strings directly. @@ -350,7 +352,7 @@ module Mongrel params[Const::PATH_INFO] = path_info params[Const::SCRIPT_NAME] = script_name params[Const::GATEWAY_INTERFACE]=Const::GATEWAY_INTERFACE_VALUE - params[Const::REMOTE_ADDR]=client.peeraddr + params[Const::REMOTE_ADDR]=client.peeraddr[3] params[Const::SERVER_NAME]=@host params[Const::SERVER_PORT]=@port params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE @@ -444,22 +446,45 @@ module Mongrel attr_reader :path # You give it the path to the directory root and an (optional) - def initialize(path, listing_allowed=true) + def initialize(path, listing_allowed=true, index_html="index.html") @path = File.expand_path(path) @listing_allowed=listing_allowed + @index_html = index_html end # Checks if the given path can be served and returns the full path (or nil if not). def can_serve(path_info) - req = File.expand_path(path_info, @path) - if req.index(@path) != 0 or !File.exist? req or (File.directory?(req) and not @listing_allowed) - return nil - else - return req + req = File.expand_path(File.join(@path,path_info), @path) + + if req.index(@path) == 0 and File.exist? req + # it exists and it's in the right location + if File.directory? req + # the request is for a directory + index = File.join(req, @index_html) + if File.exist? index + # serve the index + return index + elsif @listing_allows + # serve the directory + req + else + # do not serve anything + return nil + end + else + # it's a file and it's there + return req + end end end + + # Returns a simplistic directory listing if they're enabled, otherwise a 403. + # Base is the base URI from the REQUEST_URI, dir is the directory to serve + # on the file system (comes from can_serve()), and response is the HttpResponse + # object to send the results on. def send_dir_listing(base, dir, response) + # take off any trailing / so the links come out right base.chop! if base[-1] == "/"[-1] if @listing_allowed @@ -484,7 +509,9 @@ module Mongrel end end - + + # Sends the contents of a file back to the user. Not terribly efficient since it's + # opening and closing the file for each read. def send_file(req, response) response.start(200) do |head,out| open(req, "r") do |f| @@ -494,9 +521,10 @@ module Mongrel end + # Process the request to either serve a file or a directory listing + # if allowed (based on the listing_allowed paramter to the constructor). def process(request, response) - path_info = request.params['PATH_INFO'] - req = can_serve path_info + req = can_serve request.params['PATH_INFO'] if not req # not found, return a 404 response.start(404) do |head,out| diff --git a/test/test_uriclassifier.rb b/test/test_uriclassifier.rb index 05db8d2..b9b2778 100644 --- a/test/test_uriclassifier.rb +++ b/test/test_uriclassifier.rb @@ -115,7 +115,6 @@ class URIClassifierTest < Test::Unit::TestCase assert_equal 1, h, "wrong result for branching uri" end - def test_all_prefixing tests = ["/test","/test/that","/test/this"] uri = "/test/this/that" @@ -151,5 +150,27 @@ class URIClassifierTest < Test::Unit::TestCase assert_nil sn, "shoulnd't find anything" assert_nil pi, "shoulnd't find anything" end + + + # Verifies that a root mounted ("/") handler resolves + # such that path info matches the original URI. + # This is needed to accomodate real usage of handlers. + def test_root_mounted + u = URIClassifier.new + root = "/" + path = "/this/is/a/test" + + u.register(root, 1) + + sn, pi, h = u.resolve(root) + assert_equal 1,h, "didn't find handler" + assert_equal root,pi, "didn't get right path info" + assert_equal root,sn, "didn't get right script name" + + sn, pi, h = u.resolve(path) + assert_equal path,pi, "didn't get right path info" + assert_equal root,sn, "didn't get right script name" + assert_equal 1,h, "didn't find handler" + end end |