about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--bin/mongrel_rails21
-rw-r--r--ext/http11/http11.c22
-rw-r--r--ext/http11/tst_search.c5
-rw-r--r--lib/mongrel.rb48
-rw-r--r--test/test_uriclassifier.rb23
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