about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--examples/simpletest.rb1
-rw-r--r--ext/http11/http11.c19
-rw-r--r--ext/http11/http11_parser.c130
-rw-r--r--ext/http11/http11_parser.h6
-rw-r--r--ext/http11/http11_parser.rl16
-rw-r--r--lib/mongrel.rb290
6 files changed, 330 insertions, 132 deletions
diff --git a/examples/simpletest.rb b/examples/simpletest.rb
index 864c226..532e289 100644
--- a/examples/simpletest.rb
+++ b/examples/simpletest.rb
@@ -12,5 +12,6 @@ end
 
 h = Mongrel::HttpServer.new("0.0.0.0", "3000")
 h.register("/test", SimpleHandler.new)
+h.register("/files", Mongrel::DirHandler.new("."))
 h.run.join
 
diff --git a/ext/http11/http11.c b/ext/http11/http11.c
index c01798a..c2659c4 100644
--- a/ext/http11/http11.c
+++ b/ext/http11/http11.c
@@ -13,7 +13,7 @@ static int id_handler_map;
 
 static VALUE global_http_prefix;
 static VALUE global_request_method;
-static VALUE global_path_info;
+static VALUE global_request_uri;
 static VALUE global_query_string;
 static VALUE global_http_version;
 
@@ -45,11 +45,11 @@ void request_method(void *data, const char *at, size_t length)
   rb_hash_aset(req, global_request_method, val);
 }
 
-void path_info(void *data, const char *at, size_t length)
+void request_uri(void *data, const char *at, size_t length)
 {
   VALUE req = (VALUE)data;
   VALUE val = rb_str_new(at, length);
-  rb_hash_aset(req, global_path_info, val);
+  rb_hash_aset(req, global_request_uri, val);
 }
 
 
@@ -87,7 +87,7 @@ VALUE HttpParser_alloc(VALUE klass)
     TRACE();
     hp->http_field = http_field;
     hp->request_method = request_method;
-    hp->path_info = path_info;
+    hp->request_uri = request_uri;
     hp->query_string = query_string;
     hp->http_version = http_version;
     
@@ -279,8 +279,9 @@ VALUE URIClassifier_init(VALUE self)
  *
  * Registers the SampleHandler (one for all requests) with the "/someuri".
  * When URIClassifier::resolve is called with "/someuri" it'll return
- * SampleHandler immediately.  When "/someuri/pathhere" is called it'll
- * find SomeHandler after a second search, and setup PATH_INFO="/pathhere".
+ * SampleHandler immediately.  When called with "/someuri/iwant" it'll also
+ * return SomeHandler immediatly, with no additional searches, but it will
+ * return path info with "/iwant".
  *
  * You actually can reuse this class to register nearly anything and
  * quickly resolve it.  This could be used for caching, fast mapping, etc.
@@ -357,7 +358,7 @@ 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.  Don't try other string-line stuff yet.
+ * It expects strings with no embedded '\0' characters.  Don't try other string-line stuff yet.
  */
 VALUE URIClassifier_resolve(VALUE self, VALUE uri)
 {
@@ -401,8 +402,8 @@ void Init_http11()
   rb_global_variable(&global_http_prefix);
   global_request_method = rb_str_new2("REQUEST_METHOD");
   rb_global_variable(&global_request_method);
-  global_path_info = rb_str_new2("PATH_INFO");
-  rb_global_variable(&global_path_info);
+  global_request_uri = rb_str_new2("REQUEST_URI");
+  rb_global_variable(&global_request_uri);
   global_query_string = rb_str_new2("QUERY_STRING");
   rb_global_variable(&global_query_string);
   global_http_version = rb_str_new2("HTTP_VERSION");
diff --git a/ext/http11/http11_parser.c b/ext/http11/http11_parser.c
index c256037..83d0431 100644
--- a/ext/http11/http11_parser.c
+++ b/ext/http11/http11_parser.c
@@ -1,4 +1,4 @@
-#line 1 "ext/http11/http11_parser.rl"
+#line 1 "http11_parser.rl"
 #include "http11_parser.h"
 #include <stdio.h>
 #include <assert.h>
@@ -9,28 +9,28 @@
 #define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
 
 /** machine **/
-#line 100 "ext/http11/http11_parser.rl"
+#line 98 "http11_parser.rl"
 
 
 /** Data **/
 
-#line 18 "ext/http11/http11_parser.c"
+#line 18 "http11_parser.c"
 static int http_parser_start = 0;
 
 static int http_parser_first_final = 56;
 
 static int http_parser_error = 1;
 
-#line 104 "ext/http11/http11_parser.rl"
+#line 102 "http11_parser.rl"
 
 int http_parser_init(http_parser *parser)  {
     int cs = 0;
     
-#line 30 "ext/http11/http11_parser.c"
+#line 30 "http11_parser.c"
         {
         cs = http_parser_start;
         }
-#line 108 "ext/http11/http11_parser.rl"
+#line 106 "http11_parser.rl"
     parser->cs = cs;
     parser->body_start = NULL;
     parser->content_len = 0;
@@ -50,7 +50,7 @@ size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len)
     pe = buffer+len;
 
     
-#line 54 "ext/http11/http11_parser.c"
+#line 54 "http11_parser.c"
         {
         p -= 1;
         if ( ++p == pe )
@@ -70,14 +70,14 @@ case 0:
 st1:
         goto _out1;
 tr13:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st2;
 st2:
         if ( ++p == pe )
                 goto _out2;
 case 2:
-#line 81 "ext/http11/http11_parser.c"
+#line 81 "http11_parser.c"
         if ( (*p) == 69 )
                 goto st3;
         goto st1;
@@ -117,7 +117,7 @@ case 7:
                 goto tr33;
         goto st1;
 tr33:
-#line 29 "ext/http11/http11_parser.rl"
+#line 29 "http11_parser.rl"
         {
                if(parser->request_method != NULL)
                               parser->request_method(parser->data, parser->mark, p - parser->mark);
@@ -127,7 +127,7 @@ st8:
         if ( ++p == pe )
                 goto _out8;
 case 8:
-#line 131 "ext/http11/http11_parser.c"
+#line 131 "http11_parser.c"
         switch( (*p) ) {
                 case 42: goto tr27;
                 case 43: goto tr28;
@@ -144,26 +144,26 @@ case 8:
                 goto tr28;
         goto st1;
 tr27:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st9;
 st9:
         if ( ++p == pe )
                 goto _out9;
 case 9:
-#line 155 "ext/http11/http11_parser.c"
+#line 155 "http11_parser.c"
         if ( (*p) == 32 )
                 goto tr34;
         goto st1;
 tr34:
-#line 33 "ext/http11/http11_parser.rl"
+#line 33 "http11_parser.rl"
         {
-               if(parser->path_info != NULL)
-                              parser->path_info(parser->data, parser->mark, p - parser->mark);
+               if(parser->request_uri != NULL)
+                              parser->request_uri(parser->data, parser->mark, p - parser->mark);
         }
         goto st10;
 tr48:
-#line 37 "ext/http11/http11_parser.rl"
+#line 37 "http11_parser.rl"
         {
                if(parser->query_string != NULL)
                               parser->query_string(parser->data, parser->mark, p - parser->mark);
@@ -173,19 +173,19 @@ st10:
         if ( ++p == pe )
                 goto _out10;
 case 10:
-#line 177 "ext/http11/http11_parser.c"
+#line 177 "http11_parser.c"
         if ( (*p) == 72 )
                 goto tr11;
         goto st1;
 tr11:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st11;
 st11:
         if ( ++p == pe )
                 goto _out11;
 case 11:
-#line 189 "ext/http11/http11_parser.c"
+#line 189 "http11_parser.c"
         if ( (*p) == 84 )
                 goto st12;
         goto st1;
@@ -243,7 +243,7 @@ case 18:
                 goto st18;
         goto st1;
 tr37:
-#line 42 "ext/http11/http11_parser.rl"
+#line 42 "http11_parser.rl"
         {        
                if(parser->http_version != NULL)
                               parser->http_version(parser->data, parser->mark, p - parser->mark);
@@ -253,7 +253,7 @@ st19:
         if ( ++p == pe )
                 goto _out19;
 case 19:
-#line 257 "ext/http11/http11_parser.c"
+#line 257 "http11_parser.c"
         if ( (*p) == 10 )
                 goto st20;
         goto st1;
@@ -293,7 +293,7 @@ case 21:
                 goto tr40;
         goto st1;
 tr40:
-#line 46 "ext/http11/http11_parser.rl"
+#line 46 "http11_parser.rl"
         {
                parser->body_start = p+1; goto _out56;
         }
@@ -302,17 +302,17 @@ st56:
         if ( ++p == pe )
                 goto _out56;
 case 56:
-#line 306 "ext/http11/http11_parser.c"
+#line 306 "http11_parser.c"
         goto st1;
 tr36:
-#line 16 "ext/http11/http11_parser.rl"
+#line 16 "http11_parser.rl"
         { parser->field_start = p; }
         goto st22;
 st22:
         if ( ++p == pe )
                 goto _out22;
 case 22:
-#line 316 "ext/http11/http11_parser.c"
+#line 316 "http11_parser.c"
         switch( (*p) ) {
                 case 33: goto st22;
                 case 58: goto tr32;
@@ -338,7 +338,7 @@ case 22:
                 goto st22;
         goto st1;
 tr32:
-#line 17 "ext/http11/http11_parser.rl"
+#line 17 "http11_parser.rl"
         {
                parser->field_len = (p - parser->field_start);
         }
@@ -347,24 +347,24 @@ st23:
         if ( ++p == pe )
                 goto _out23;
 case 23:
-#line 351 "ext/http11/http11_parser.c"
+#line 351 "http11_parser.c"
         if ( (*p) == 13 )
                 goto tr56;
         goto tr55;
 tr55:
-#line 21 "ext/http11/http11_parser.rl"
+#line 21 "http11_parser.rl"
         { MARK(parser, p); }
         goto st24;
 st24:
         if ( ++p == pe )
                 goto _out24;
 case 24:
-#line 363 "ext/http11/http11_parser.c"
+#line 363 "http11_parser.c"
         if ( (*p) == 13 )
                 goto tr51;
         goto st24;
 tr51:
-#line 22 "ext/http11/http11_parser.rl"
+#line 22 "http11_parser.rl"
         {
                if(parser->http_field != NULL) {
                               parser->http_field(parser->data,
@@ -374,9 +374,9 @@ tr51:
         }
         goto st25;
 tr56:
-#line 21 "ext/http11/http11_parser.rl"
+#line 21 "http11_parser.rl"
         { MARK(parser, p); }
-#line 22 "ext/http11/http11_parser.rl"
+#line 22 "http11_parser.rl"
         {
                if(parser->http_field != NULL) {
                               parser->http_field(parser->data,
@@ -389,7 +389,7 @@ st25:
         if ( ++p == pe )
                 goto _out25;
 case 25:
-#line 393 "ext/http11/http11_parser.c"
+#line 393 "http11_parser.c"
         switch( (*p) ) {
                 case 10: goto st26;
                 case 13: goto tr51;
@@ -424,14 +424,14 @@ case 26:
                 goto tr42;
         goto st24;
 tr42:
-#line 16 "ext/http11/http11_parser.rl"
+#line 16 "http11_parser.rl"
         { parser->field_start = p; }
         goto st27;
 st27:
         if ( ++p == pe )
                 goto _out27;
 case 27:
-#line 435 "ext/http11/http11_parser.c"
+#line 435 "http11_parser.c"
         switch( (*p) ) {
                 case 13: goto tr51;
                 case 33: goto st27;
@@ -458,14 +458,14 @@ case 27:
                 goto st27;
         goto st24;
 tr28:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st28;
 st28:
         if ( ++p == pe )
                 goto _out28;
 case 28:
-#line 469 "ext/http11/http11_parser.c"
+#line 469 "http11_parser.c"
         switch( (*p) ) {
                 case 43: goto st28;
                 case 58: goto st29;
@@ -483,14 +483,14 @@ case 28:
                 goto st28;
         goto st1;
 tr30:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st29;
 st29:
         if ( ++p == pe )
                 goto _out29;
 case 29:
-#line 494 "ext/http11/http11_parser.c"
+#line 494 "http11_parser.c"
         switch( (*p) ) {
                 case 32: goto tr34;
                 case 37: goto st30;
@@ -531,14 +531,14 @@ case 31:
                 goto st29;
         goto st1;
 tr29:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st32;
 st32:
         if ( ++p == pe )
                 goto _out32;
 case 32:
-#line 542 "ext/http11/http11_parser.c"
+#line 542 "http11_parser.c"
         switch( (*p) ) {
                 case 32: goto tr34;
                 case 37: goto st34;
@@ -599,17 +599,17 @@ case 35:
                 goto st33;
         goto st1;
 tr46:
-#line 33 "ext/http11/http11_parser.rl"
+#line 33 "http11_parser.rl"
         {
-               if(parser->path_info != NULL)
-                              parser->path_info(parser->data, parser->mark, p - parser->mark);
+               if(parser->request_uri != NULL)
+                              parser->request_uri(parser->data, parser->mark, p - parser->mark);
         }
         goto st36;
 st36:
         if ( ++p == pe )
                 goto _out36;
 case 36:
-#line 613 "ext/http11/http11_parser.c"
+#line 613 "http11_parser.c"
         switch( (*p) ) {
                 case 32: goto tr48;
                 case 37: goto tr54;
@@ -624,14 +624,14 @@ case 36:
                 goto st1;
         goto tr53;
 tr53:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st37;
 st37:
         if ( ++p == pe )
                 goto _out37;
 case 37:
-#line 635 "ext/http11/http11_parser.c"
+#line 635 "http11_parser.c"
         switch( (*p) ) {
                 case 32: goto tr48;
                 case 37: goto st38;
@@ -646,14 +646,14 @@ case 37:
                 goto st1;
         goto st37;
 tr54:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st38;
 st38:
         if ( ++p == pe )
                 goto _out38;
 case 38:
-#line 657 "ext/http11/http11_parser.c"
+#line 657 "http11_parser.c"
         if ( (*p) < 65 ) {
                 if ( 48 <= (*p) && (*p) <= 57 )
                         goto st39;
@@ -677,14 +677,14 @@ case 39:
                 goto st37;
         goto st1;
 tr14:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st40;
 st40:
         if ( ++p == pe )
                 goto _out40;
 case 40:
-#line 688 "ext/http11/http11_parser.c"
+#line 688 "http11_parser.c"
         if ( (*p) == 69 )
                 goto st41;
         goto st1;
@@ -696,14 +696,14 @@ case 41:
                 goto st7;
         goto st1;
 tr15:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st42;
 st42:
         if ( ++p == pe )
                 goto _out42;
 case 42:
-#line 707 "ext/http11/http11_parser.c"
+#line 707 "http11_parser.c"
         if ( (*p) == 69 )
                 goto st43;
         goto st1;
@@ -722,14 +722,14 @@ case 44:
                 goto st7;
         goto st1;
 tr16:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st45;
 st45:
         if ( ++p == pe )
                 goto _out45;
 case 45:
-#line 733 "ext/http11/http11_parser.c"
+#line 733 "http11_parser.c"
         if ( (*p) == 80 )
                 goto st46;
         goto st1;
@@ -769,14 +769,14 @@ case 50:
                 goto st7;
         goto st1;
 tr17:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st51;
 st51:
         if ( ++p == pe )
                 goto _out51;
 case 51:
-#line 780 "ext/http11/http11_parser.c"
+#line 780 "http11_parser.c"
         switch( (*p) ) {
                 case 79: goto st52;
                 case 85: goto st41;
@@ -790,14 +790,14 @@ case 52:
                 goto st41;
         goto st1;
 tr18:
-#line 14 "ext/http11/http11_parser.rl"
+#line 14 "http11_parser.rl"
         { MARK(parser, p); }
         goto st53;
 st53:
         if ( ++p == pe )
                 goto _out53;
 case 53:
-#line 801 "ext/http11/http11_parser.c"
+#line 801 "http11_parser.c"
         if ( (*p) == 82 )
                 goto st54;
         goto st1;
@@ -875,15 +875,15 @@ case 55:
 
         _out: {}
         }
-#line 127 "ext/http11/http11_parser.rl"
+#line 125 "http11_parser.rl"
 
     parser->cs = cs;
     parser->nread = p - buffer;
     if(parser->body_start) {
         /* final \r\n combo encountered so stop right here */
         
-#line 886 "ext/http11/http11_parser.c"
-#line 133 "ext/http11/http11_parser.rl"
+#line 886 "http11_parser.c"
+#line 131 "http11_parser.rl"
         parser->nread++;
     }
 
@@ -895,8 +895,8 @@ int http_parser_finish(http_parser *parser)
         int cs = parser->cs;
 
         
-#line 899 "ext/http11/http11_parser.c"
-#line 144 "ext/http11/http11_parser.rl"
+#line 899 "http11_parser.c"
+#line 142 "http11_parser.rl"
 
         parser->cs = cs;
 
diff --git a/ext/http11/http11_parser.h b/ext/http11/http11_parser.h
index f4a205f..6ca9cef 100644
--- a/ext/http11/http11_parser.h
+++ b/ext/http11/http11_parser.h
@@ -3,6 +3,10 @@
 
 #include <sys/types.h>
 
+#if defined(_WIN32)
+#include <stddef.h>
+#endif
+
 typedef void (*element_cb)(void *data, const char *at, size_t length);
 typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
 
@@ -19,7 +23,7 @@ typedef struct http_parser {
 
   field_cb http_field;
   element_cb request_method;
-  element_cb path_info;
+  element_cb request_uri;
   element_cb query_string;
   element_cb http_version;
 
diff --git a/ext/http11/http11_parser.rl b/ext/http11/http11_parser.rl
index 7642cb1..c86be7a 100644
--- a/ext/http11/http11_parser.rl
+++ b/ext/http11/http11_parser.rl
@@ -30,9 +30,9 @@
                if(parser->request_method != NULL)
                               parser->request_method(parser->data, parser->mark, p - parser->mark);
         }
-        action path_info {
-               if(parser->path_info != NULL)
-                              parser->path_info(parser->data, parser->mark, p - parser->mark);
+        action request_uri {
+               if(parser->request_uri != NULL)
+                              parser->request_uri(parser->data, parser->mark, p - parser->mark);
         }
         action query_string {
                if(parser->query_string != NULL)
@@ -70,23 +70,21 @@
 
         # URI schemes and absolute paths
         scheme = ( alpha | digit | "+" | "-" | "." )* ;
-        absolute_uri = (scheme ":" (uchar | reserved )*) >mark %path_info;
+        absolute_uri = (scheme ":" (uchar | reserved )*) >mark %request_uri;
 
         path = (pchar+ ( "/" pchar* )*) ;
         query = ( uchar | reserved )* >mark %query_string ;
         param = ( pchar | "/" )* ;
         params = (param ( ";" param )*) ;
-        rel_path = (path? (";" params)?) %path_info  ("?" query)? ;
+        rel_path = (path? (";" params)?) %request_uri  ("?" query)? ;
         absolute_path = ("/" rel_path) >mark ;
         
-        Request_URI = ("*" >mark %path_info | absolute_uri | absolute_path) ;
+        Request_URI = ("*" >mark %request_uri | absolute_uri | absolute_path) ;
         Method = ("OPTIONS"| "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "TRACE") >mark %request_method;
         
         http_number = (digit+ "." digit+) ;
         HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
         Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ;
-
-      
         
         field_name = (token - ":")+ >start_field %write_field;
 
@@ -96,7 +94,7 @@
         
         Request = Request_Line (message_header)* $0 ( CRLF $1 @done );
 
-                main := Request;
+        main := Request;
 }%%
 
 /** Data **/
diff --git a/lib/mongrel.rb b/lib/mongrel.rb
index 4912b8a..4b59549 100644
--- a/lib/mongrel.rb
+++ b/lib/mongrel.rb
@@ -8,6 +8,9 @@ require 'stringio'
 # functionality to service web application requests fast as possible.
 module Mongrel
 
+  # Every standard HTTP code mapped to the appropriate message.  These are
+  # used so frequently that they are placed directly in Mongrel for easy
+  # access rather than Mongrel::Const.
   HTTP_STATUS_CODES = {  
     100  => 'Continue',
     101  => 'Switching Protocols',
@@ -48,19 +51,89 @@ 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.
+  # Symbols did not really improve things much compared to constants.
+  #
+  # While Mongrel does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
+  # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
+  # too taxing on performance.
+  module Const
+    # This is the part of the path after the SCRIPT_NAME.  URIClassifier will determine this.
+    PATH_INFO="PATH_INFO"
+    # This is the intial part that your handler is identified as by URIClassifier.
+    SCRIPT_NAME="SCRIPT_NAME"
+    # The original URI requested by the client.  Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
+    REQUEST_URI='REQUEST_URI'
+
+    # Content length (also available as HTTP_CONTENT_LENGTH).
+    CONTENT_LENGTH='CONTENT_LENGTH'
+
+    # Content length (also available as CONTENT_LENGTH).
+    HTTP_CONTENT_LENGTH='HTTP_CONTENT_LENGTH'
+
+    # Content type (also available as HTTP_CONTENT_TYPE).
+    CONTENT_TYPE='CONTENT_TYPE'
+
+    # Content type (also available as CONTENT_TYPE).
+    HTTP_CONTENT_TYPE='HTTP_CONTENT_TYPE'
+
+    # Gateway interface key in the HttpRequest parameters.
+    GATEWAY_INTERFACE='GATEWAY_INTERFACE'
+    # We claim to support CGI/1.2.
+    GATEWAY_INTERFACE_VALUE='CGI/1.2'
+
+    # Hosts remote IP address.  Mongrel does not do DNS resolves since that slows
+    # processing down considerably.
+    REMOTE_ADDR='REMOTE_ADDR'
+
+    # This is not given since Mongrel does not do DNS resolves.  It is only here for
+    # completeness for the CGI standard.
+    REMOTE_HOST='REMOTE_HOST'
+
+    # The name/host of our server as given by the HttpServer.new(host,port) call.
+    SERVER_NAME='SERVER_NAME'
+
+    # The port of our server as given by the HttpServer.new(host,port) call.
+    SERVER_PORT='SERVER_PORT'
+
+    # Official server protocol key in the HttpRequest parameters.
+    SERVER_PROTOCOL='SERVER_PROTOCOL'
+    # Mongrel claims to support HTTP/1.1.
+    SERVER_PROTOCOL_VALUE='HTTP/1.1'
+
+    # The actual server software being used (it's Mongrel man).
+    SERVER_SOFTWARE='SERVER_SOFTWARE'
+    
+    # Current Mongrel version (used for SERVER_SOFTWARE and other response headers).
+    MONGREL_VERSION='Mongrel 0.2.2'
+
+    # The standard empty 404 response for bad requests.  Use Error4040Handler for custom stuff.
+    ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
+
+    # A common header for indicating the server is too busy.  Not used yet.
+    ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
+
+    # The basic max request size we'll try to read.
+    CHUNK_SIZE=(16 * 1024)
+
+  end
+
+
   # When a handler is found for a registered URI then this class is constructed
   # and passed to your HttpHandler::process method.  You should assume that
   # *one* handler processes all requests.  Included in the HttpReqeust is a
   # HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
   # which is a string containing the request body (raw for now).
   #
-  # Mongrel really only support small-ish request bodies right now since really
+  # Mongrel really only supports small-ish request bodies right now since really
   # huge ones have to be completely read off the wire and put into a string.
   # Later there will be several options for efficiently handling large file
   # uploads.
   class HttpRequest
     attr_reader :body, :params
-    
+
     # You don't really call this.  It's made for you.
     # Main thing it does is hook up the params, and store any remaining
     # body data into the HttpRequest.body attribute.
@@ -68,13 +141,14 @@ module Mongrel
       @body = initial_body || ""
       @params = params
       @socket = socket
-
+      
       # fix up the CGI requirements
-      params['CONTENT_LENGTH'] = params['HTTP_CONTENT_LENGTH'] || 0
+      params[Const::CONTENT_LENGTH] = params[Const::HTTP_CONTENT_LENGTH] || 0
+      params[Const::CONTENT_TYPE] ||= params[Const::HTTP_CONTENT_TYPE]
 
       # now, if the initial_body isn't long enough for the content length we have to fill it
       # TODO: adapt for big ass stuff by writing to a temp file
-      clen = params['HTTP_CONTENT_LENGTH'].to_i
+      clen = params[Const::HTTP_CONTENT_LENGTH].to_i
       if @body.length < clen
         @body << @socket.read(clen - @body.length)
       end
@@ -82,6 +156,13 @@ module Mongrel
   end
 
 
+  # This class implements a simple way of constructing the HTTP headers dynamically
+  # via a Hash syntax.  Think of it as a write-only Hash.  Refer to HttpResponse for
+  # information on how this is used.
+  #
+  # One consequence of this write-only nature is that you can write multiple headers
+  # by just doing them twice (which is sometimes needed in HTTP), but that the normal
+  # semantics for Hash (where doing an insert replaces) is not there.
   class HeaderOut
     attr_reader :out
 
@@ -89,6 +170,7 @@ module Mongrel
       @out = out
     end
 
+    # Simply writes "#{key}: #{value}" to an output buffer.
     def[]=(key,value)
       @out.write(key)
       @out.write(": ")
@@ -97,7 +179,35 @@ module Mongrel
     end
   end
 
-
+  # Writes and controls your response to the client using the HTTP/1.1 specification.
+  # You use it by simply doing:
+  #
+  #  response.start(200) do |head,out|
+  #    head['Content-Type'] = 'text/plain'
+  #    out.write("hello\n")
+  #  end
+  #
+  # The parameter to start is the response code--which Mongrel will translate for you
+  # based on HTTP_STATUS_CODES.  The head parameter is how you write custom headers.
+  # The out parameter is where you write your body.  The default status code for
+  # HttpResponse.start is 200 so the above example is redundant.
+  #
+  # As you can see, it's just like using a Hash and as you do this it writes the proper
+  # header to the output on the fly.  You can even intermix specifying headers and
+  # writing content.  The HttpResponse class with write the things in the proper order
+  # once the HttpResponse.block is ended.
+  #
+  # You may also work the HttpResponse object directly using the various attributes available
+  # for the raw socket, body, header, and status codes.  If you do this you're on your own.
+  # A design decision was made to force the client to not pipeline requests.  HTTP/1.1
+  # pipelining really kills the performance due to how it has to be handled and how
+  # unclear the standard is.  To fix this the HttpResponse gives a "Connection: close"
+  # header which forces the client to close right away.  The bonus for this is that it
+  # gives a pretty nice speed boost to most clients since they can close their connection
+  # immediately.
+  #
+  # One additional caveat is that you don't have to specify the Content-length header
+  # as the HttpResponse will write this for you based on the out length.
   class HttpResponse
     attr_reader :socket
     attr_reader :body
@@ -112,12 +222,25 @@ module Mongrel
       @header = HeaderOut.new(StringIO.new)
     end
 
+    # Receives a block passing it the header and body for you to work with.
+    # When the block is finished it writes everything you've done to
+    # the socket in the proper order.  This lets you intermix header and
+    # body content as needed.
     def start(status=200)
       @status = status
       yield @header, @body
       finished
     end
-    
+
+    # Primarily used in exception handling to reset the response output in order to write
+    # an alternative response.
+    def reset
+      @header.out.rewind
+      @body.rewind
+    end
+
+    # This takes whatever has been done to header and body and then writes it in the
+    # proper format to make an HTTP/1.1 response.
     def finished
       @header.out.rewind
       @body.rewind
@@ -136,29 +259,8 @@ module Mongrel
   # a response.  Look at the HttpRequest and HttpResponse objects for how
   # to use them.
   class HttpHandler
-    attr_accessor :script_name
-
-    def process(request, response)
-    end
-  end
-
-
-  # The server normally returns a 404 response if a URI is requested, but it
-  # also returns a lame empty message.  This lets you do a 404 response
-  # with a custom message for special URIs.
-  class Error404Handler < HttpHandler
-
-    # Sets the message to return.  This is constructed once for the handler
-    # so it's pretty efficient.
-    def initialize(msg)
-      @response = HttpServer::ERROR_404_RESPONSE + msg
-    end
-    
-    # Just kicks back the standard 404 response with your special message.
     def process(request, response)
-      response.socket.write(@response)
     end
-
   end
 
 
@@ -183,16 +285,6 @@ module Mongrel
   class HttpServer
     attr_reader :acceptor
 
-    # The standard empty 404 response for bad requests.  Use Error4040Handler for custom stuff.
-    ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel/0.2\r\n\r\nNOT FOUND"
-    ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
-
-    # The basic max request size we'll try to read.
-    CHUNK_SIZE=(16 * 1024)
-
-    PATH_INFO="PATH_INFO"
-    SCRIPT_NAME="SCRIPT_NAME"
-    
     # Creates a working server on host:port (strange things happen if port isn't a Number).
     # Use HttpServer::run to start the server.
     #
@@ -210,8 +302,13 @@ module Mongrel
     # Future versions of Mongrel will make this more dynamic (hopefully).
     def initialize(host, port, num_processors=20)
       @socket = TCPServer.new(host, port)
+
       @classifier = URIClassifier.new
       @req_queue = Queue.new
+      @host = host
+      @port = port
+      @num_procesors = num_processors
+
       num_processors.times {|i| Thread.new do
           while client = @req_queue.deq
             process_client(client)
@@ -223,30 +320,35 @@ module Mongrel
 
     # Does the majority of the IO processing.  It has been written in Ruby using
     # about 7 different IO processing strategies and no matter how it's done
-    # the performance just does not improve.  Ruby's use of select to implement
-    # threads means that it will most likely never improve, so the only remaining
-    # approach is to write all or some of this function in C.  That will be the
-    # focus of future releases.
+    # the performance just does not improve.  It is currently carefully constructed
+    # to make sure that it gets the best possible performance, but anyone who
+    # thinks they can make it faster is more than welcome to take a crack at it.
     def process_client(client)
       begin
         parser = HttpParser.new
         params = {}
-        data = client.readpartial(CHUNK_SIZE)
+        data = client.readpartial(Const::CHUNK_SIZE)
 
         while true
           nread = parser.execute(params, data)
           if parser.finished?
-            script_name, path_info, handler = @classifier.resolve(params[PATH_INFO])
+            script_name, path_info, handler = @classifier.resolve(params[Const::REQUEST_URI])
 
             if handler
-              params[PATH_INFO] = path_info
-              params[SCRIPT_NAME] = script_name
+              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::SERVER_NAME]=@host
+              params[Const::SERVER_PORT]=@port
+              params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE
+              params[Const::SERVER_SOFTWARE]=Const::MONGREL_VERSION
 
               request = HttpRequest.new(params, data[nread ... data.length], client)
               response = HttpResponse.new(client)
               handler.process(request, response)
             else
-              client.write(ERROR_404_RESPONSE)
+              client.write(Const::ERROR_404_RESPONSE)
             end
 
             break
@@ -254,7 +356,7 @@ module Mongrel
             # gotta stream and read again until we can get the parser to be character safe
             # TODO: make this more efficient since this means we're parsing a lot repeatedly
             parser.reset
-            data << client.readpartial(CHUNK_SIZE)
+            data << client.readpartial(Const::CHUNK_SIZE)
           end
         end
       rescue EOFError
@@ -274,6 +376,7 @@ module Mongrel
     # Runs the thing.  It returns the thread used so you can "join" it.  You can also
     # access the HttpServer::acceptor attribute to get the thread later.
     def run
+      BasicSocket.do_not_reverse_lookup=true
       @acceptor = Thread.new do
         while true
           @req_queue << @socket.accept
@@ -295,4 +398,95 @@ module Mongrel
       @classifier.unregister(uri)
     end
   end
+
+
+  # The server normally returns a 404 response if a URI is requested, but it
+  # also returns a lame empty message.  This lets you do a 404 response
+  # with a custom message for special URIs.
+  class Error404Handler < HttpHandler
+
+    # Sets the message to return.  This is constructed once for the handler
+    # so it's pretty efficient.
+    def initialize(msg)
+      @response = HttpServer::ERROR_404_RESPONSE + msg
+    end
+    
+    # Just kicks back the standard 404 response with your special message.
+    def process(request, response)
+      response.socket.write(@response)
+    end
+
+  end
+
+
+  # Serves the contents of a directory.  You give it the path to the root
+  # where the files are located, and it tries to find the files based on
+  # the PATH_INFO inside the directory.  If the requested path is a
+  # directory then it returns a simple directory listing.
+  #
+  # It does a simple protection against going outside it's root path by
+  # converting all paths to an absolute expanded path, and then making sure
+  # that the final expanded path includes the root path.  If it doesn't
+  # than it simply gives a 404.
+  class DirHandler < HttpHandler
+
+    def initialize(path, listing_allowed=true)
+      @path = File.expand_path(path)
+      @listing_allowed=listing_allowed
+      puts "DIR: #@path"
+    end
+
+    def send_dir_listing(base, dir, response)
+      if @listing_allowed
+        response.start(200) do |head,out|
+          head['Content-Type'] = "text/html"
+          out << "<html><head><title>Directory Listing</title></head><body>"
+          Dir.entries(dir).each do |child|
+            out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>"
+          end
+          out << "</body></html>"
+        end
+      else
+        response.start(403) do |head,out|
+          out.write("Directory listings not allowed")
+        end
+      end
+    end
+
+
+    def send_file(req, response)
+      response.start(200) do |head,out|
+        open(req, "r") do |f|
+          out.write(f.read)
+        end
+      end
+    end
+
+
+    def process(request, response)
+      req = File.expand_path("." + request.params['PATH_INFO'], @path)
+      puts "FIND: #{req}"
+      if req.index(@path) != 0 or !File.exist? req
+        # not found, return a 404
+        response.start(404) do |head,out|
+          out << "File not found"
+        end
+      else
+        begin
+          if File.directory? req
+            send_dir_listing(request.params["REQUEST_URI"],req, response)
+          else
+            send_file(req, response)
+          end
+        rescue => details
+          response.reset
+          response.start(403) do |head,out|
+            out << "Error accessing file"
+          end
+          STDERR.puts "ERROR: #{details}"
+        end
+      end
+    end
+  end
+
 end