mongrel 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/README +10 -2
  2. data/Rakefile +1 -1
  3. data/doc/rdoc/classes/Mongrel.html +11 -1
  4. data/doc/rdoc/classes/Mongrel/Const.html +342 -0
  5. data/doc/rdoc/classes/Mongrel/DirHandler.html +201 -0
  6. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000008.html +20 -0
  7. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000009.html +31 -0
  8. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000010.html +22 -0
  9. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000011.html +39 -0
  10. data/doc/rdoc/classes/Mongrel/Error404Handler.html +10 -10
  11. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000023.html → M000028.html} +4 -4
  12. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000024.html → M000029.html} +4 -4
  13. data/doc/rdoc/classes/Mongrel/HeaderOut.html +28 -10
  14. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000013.html → M000017.html} +4 -4
  15. data/doc/rdoc/classes/Mongrel/HeaderOut.src/M000018.html +21 -0
  16. data/doc/rdoc/classes/Mongrel/HttpHandler.html +5 -18
  17. data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000019.html → M000023.html} +3 -3
  18. data/doc/rdoc/classes/Mongrel/HttpRequest.html +8 -8
  19. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000030.html +31 -0
  20. data/doc/rdoc/classes/Mongrel/HttpResponse.html +89 -15
  21. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000020.html → M000024.html} +7 -7
  22. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000021.html → M000025.html} +6 -6
  23. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000026.html +19 -0
  24. data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000022.html → M000027.html} +11 -11
  25. data/doc/rdoc/classes/Mongrel/HttpServer.html +32 -76
  26. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000012.html +18 -5
  27. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000013.html +64 -0
  28. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000010.html → M000014.html} +9 -8
  29. data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000011.html → M000015.html} +4 -4
  30. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000016.html +18 -0
  31. data/doc/rdoc/classes/Mongrel/URIClassifier.html +25 -23
  32. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000015.html → M000019.html} +0 -0
  33. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000016.html → M000020.html} +3 -2
  34. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000017.html → M000021.html} +0 -0
  35. data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000018.html → M000022.html} +1 -1
  36. data/doc/rdoc/created.rid +1 -1
  37. data/doc/rdoc/files/README.html +14 -2
  38. data/doc/rdoc/files/ext/http11/http11_c.html +1 -1
  39. data/doc/rdoc/files/lib/mongrel_rb.html +1 -1
  40. data/doc/rdoc/fr_class_index.html +2 -0
  41. data/doc/rdoc/fr_method_index.html +23 -18
  42. data/examples/simpletest.rb +2 -1
  43. data/ext/http11/http11.c +10 -9
  44. data/ext/http11/http11_parser.c +10 -10
  45. data/ext/http11/http11_parser.h +5 -1
  46. data/lib/#mongrel.rb# +493 -0
  47. data/lib/mongrel.rb +242 -48
  48. metadata +28 -19
  49. data/doc/rdoc/classes/Mongrel/HeaderOut.src/M000014.html +0 -21
  50. data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000025.html +0 -30
  51. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000008.html +0 -26
  52. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000009.html +0 -58
@@ -16,8 +16,9 @@
16
16
  *
17
17
  * Registers the SampleHandler (one for all requests) with the "/someuri".
18
18
  * When URIClassifier::resolve is called with "/someuri" it'll return
19
- * SampleHandler immediately. When "/someuri/pathhere" is called it'll
20
- * find SomeHandler after a second search, and setup PATH_INFO="/pathhere".
19
+ * SampleHandler immediately. When called with "/someuri/iwant" it'll also
20
+ * return SomeHandler immediatly, with no additional searches, but it will
21
+ * return path info with "/iwant".
21
22
  *
22
23
  * You actually can reuse this class to register nearly anything and
23
24
  * quickly resolve it. This could be used for caching, fast mapping, etc.
@@ -32,7 +32,7 @@
32
32
  * It also means that it's very efficient to do this only taking as long as the URI has
33
33
  * characters.
34
34
  *
35
- * It expects strings. Don't try other string-line stuff yet.
35
+ * It expects strings with no embedded '\0' characters. Don't try other string-line stuff yet.
36
36
  */
37
37
  VALUE URIClassifier_resolve(VALUE self, VALUE uri)
38
38
  {
@@ -1 +1 @@
1
- Thu Feb 02 01:57:10 EST 2006
1
+ Fri Feb 03 01:14:07 EST 2006
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Feb 02 01:53:46 EST 2006</td>
59
+ <td>Fri Feb 03 01:13:51 EST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -84,7 +84,7 @@ portability issues.
84
84
  </p>
85
85
  <h2>Status</h2>
86
86
  <p>
87
- The 0.2.1 release of <a href="../classes/Mongrel.html">Mongrel</a> features
87
+ The 0.2.2 release of <a href="../classes/Mongrel.html">Mongrel</a> features
88
88
  an HTTP core server that is the fastest possible thing I could get without
89
89
  using something other than Ruby. It features a few bug fixes, but mostly
90
90
  just a change to the <a
@@ -111,6 +111,12 @@ thanks to Tom Copland. I&#8217;ll be looking to automate management of
111
111
  this, but feel free to use rubyforge to post feature requests, bugs, and
112
112
  join the mailing list.
113
113
  </p>
114
+ <p>
115
+ Finally, it now supports all CGI parameters that don&#8217;t cause a
116
+ performance hit, and it has a <a
117
+ href="../classes/Mongrel/DirHandler.html">Mongrel::DirHandler</a> which can
118
+ serve files out of a directory and do (optional) directory listings.
119
+ </p>
114
120
  <h2>Install</h2>
115
121
  <p>
116
122
  It doesn&#8217;t explicitly require Camping, but if you want to run the
@@ -143,6 +149,7 @@ example:
143
149
 
144
150
  h = Mongrel::HttpServer.new(&quot;0.0.0.0&quot;, &quot;3000&quot;)
145
151
  h.register(&quot;/test&quot;, SimpleHandler.new)
152
+ h.register(&quot;/files&quot;, DirHandler.new(&quot;.&quot;))
146
153
  h.run.join
147
154
  </pre>
148
155
  <p>
@@ -152,6 +159,11 @@ If you run this and access port 3000 with a browser it will say
152
159
  href="../classes/Mongrel/Error404Handler.html">Mongrel::Error404Handler</a>
153
160
  for a basic way to give a more complex 404 message.
154
161
  </p>
162
+ <p>
163
+ This also shows the DirHandler with directory listings. This is still rough
164
+ but it should work for basic hosting. *File extension to mime type mapping
165
+ is missing though.*
166
+ </p>
155
167
  <h2>Speed</h2>
156
168
  <p>
157
169
  The 0.2.1 release probably consists of the most effort I&#8217;ve ever put
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Thu Feb 02 00:12:35 EST 2006</td>
59
+ <td>Fri Feb 03 00:55:27 EST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -56,7 +56,7 @@
56
56
  </tr>
57
57
  <tr class="top-aligned-row">
58
58
  <td><strong>Last Update:</strong></td>
59
- <td>Mon Jan 30 23:59:49 EST 2006</td>
59
+ <td>Fri Feb 03 00:55:27 EST 2006</td>
60
60
  </tr>
61
61
  </table>
62
62
  </div>
@@ -21,6 +21,8 @@
21
21
  <h1 class="section-bar">Classes</h1>
22
22
  <div id="index-entries">
23
23
  <a href="classes/Mongrel.html">Mongrel</a><br />
24
+ <a href="classes/Mongrel/Const.html">Mongrel::Const</a><br />
25
+ <a href="classes/Mongrel/DirHandler.html">Mongrel::DirHandler</a><br />
24
26
  <a href="classes/Mongrel/Error404Handler.html">Mongrel::Error404Handler</a><br />
25
27
  <a href="classes/Mongrel/HeaderOut.html">Mongrel::HeaderOut</a><br />
26
28
  <a href="classes/Mongrel/HttpHandler.html">Mongrel::HttpHandler</a><br />
@@ -20,31 +20,36 @@
20
20
  <div id="index">
21
21
  <h1 class="section-bar">Methods</h1>
22
22
  <div id="index-entries">
23
- <a href="classes/Mongrel/HeaderOut.html#M000014">[]= (Mongrel::HeaderOut)</a><br />
23
+ <a href="classes/Mongrel/HeaderOut.html#M000018">[]= (Mongrel::HeaderOut)</a><br />
24
24
  <a href="classes/Mongrel/HttpParser.html#M000005">error? (Mongrel::HttpParser)</a><br />
25
25
  <a href="classes/Mongrel/HttpParser.html#M000004">execute (Mongrel::HttpParser)</a><br />
26
26
  <a href="classes/Mongrel/HttpParser.html#M000003">finish (Mongrel::HttpParser)</a><br />
27
- <a href="classes/Mongrel/HttpResponse.html#M000022">finished (Mongrel::HttpResponse)</a><br />
27
+ <a href="classes/Mongrel/HttpResponse.html#M000027">finished (Mongrel::HttpResponse)</a><br />
28
28
  <a href="classes/Mongrel/HttpParser.html#M000006">finished? (Mongrel::HttpParser)</a><br />
29
- <a href="classes/Mongrel/Error404Handler.html#M000023">new (Mongrel::Error404Handler)</a><br />
30
- <a href="classes/Mongrel/HttpServer.html#M000008">new (Mongrel::HttpServer)</a><br />
31
- <a href="classes/Mongrel/HttpRequest.html#M000025">new (Mongrel::HttpRequest)</a><br />
32
- <a href="classes/Mongrel/HttpResponse.html#M000020">new (Mongrel::HttpResponse)</a><br />
33
- <a href="classes/Mongrel/URIClassifier.html#M000015">new (Mongrel::URIClassifier)</a><br />
34
29
  <a href="classes/Mongrel/HttpParser.html#M000001">new (Mongrel::HttpParser)</a><br />
35
- <a href="classes/Mongrel/HeaderOut.html#M000013">new (Mongrel::HeaderOut)</a><br />
30
+ <a href="classes/Mongrel/DirHandler.html#M000008">new (Mongrel::DirHandler)</a><br />
31
+ <a href="classes/Mongrel/HttpResponse.html#M000024">new (Mongrel::HttpResponse)</a><br />
32
+ <a href="classes/Mongrel/URIClassifier.html#M000019">new (Mongrel::URIClassifier)</a><br />
33
+ <a href="classes/Mongrel/Error404Handler.html#M000028">new (Mongrel::Error404Handler)</a><br />
34
+ <a href="classes/Mongrel/HttpServer.html#M000012">new (Mongrel::HttpServer)</a><br />
35
+ <a href="classes/Mongrel/HeaderOut.html#M000017">new (Mongrel::HeaderOut)</a><br />
36
+ <a href="classes/Mongrel/HttpRequest.html#M000030">new (Mongrel::HttpRequest)</a><br />
36
37
  <a href="classes/Mongrel/HttpParser.html#M000007">nread (Mongrel::HttpParser)</a><br />
37
- <a href="classes/Mongrel/Error404Handler.html#M000024">process (Mongrel::Error404Handler)</a><br />
38
- <a href="classes/Mongrel/HttpHandler.html#M000019">process (Mongrel::HttpHandler)</a><br />
39
- <a href="classes/Mongrel/HttpServer.html#M000009">process_client (Mongrel::HttpServer)</a><br />
40
- <a href="classes/Mongrel/HttpServer.html#M000011">register (Mongrel::HttpServer)</a><br />
41
- <a href="classes/Mongrel/URIClassifier.html#M000016">register (Mongrel::URIClassifier)</a><br />
38
+ <a href="classes/Mongrel/Error404Handler.html#M000029">process (Mongrel::Error404Handler)</a><br />
39
+ <a href="classes/Mongrel/DirHandler.html#M000011">process (Mongrel::DirHandler)</a><br />
40
+ <a href="classes/Mongrel/HttpHandler.html#M000023">process (Mongrel::HttpHandler)</a><br />
41
+ <a href="classes/Mongrel/HttpServer.html#M000013">process_client (Mongrel::HttpServer)</a><br />
42
+ <a href="classes/Mongrel/HttpServer.html#M000015">register (Mongrel::HttpServer)</a><br />
43
+ <a href="classes/Mongrel/URIClassifier.html#M000020">register (Mongrel::URIClassifier)</a><br />
44
+ <a href="classes/Mongrel/HttpResponse.html#M000026">reset (Mongrel::HttpResponse)</a><br />
42
45
  <a href="classes/Mongrel/HttpParser.html#M000002">reset (Mongrel::HttpParser)</a><br />
43
- <a href="classes/Mongrel/URIClassifier.html#M000018">resolve (Mongrel::URIClassifier)</a><br />
44
- <a href="classes/Mongrel/HttpServer.html#M000010">run (Mongrel::HttpServer)</a><br />
45
- <a href="classes/Mongrel/HttpResponse.html#M000021">start (Mongrel::HttpResponse)</a><br />
46
- <a href="classes/Mongrel/URIClassifier.html#M000017">unregister (Mongrel::URIClassifier)</a><br />
47
- <a href="classes/Mongrel/HttpServer.html#M000012">unregister (Mongrel::HttpServer)</a><br />
46
+ <a href="classes/Mongrel/URIClassifier.html#M000022">resolve (Mongrel::URIClassifier)</a><br />
47
+ <a href="classes/Mongrel/HttpServer.html#M000014">run (Mongrel::HttpServer)</a><br />
48
+ <a href="classes/Mongrel/DirHandler.html#M000009">send_dir_listing (Mongrel::DirHandler)</a><br />
49
+ <a href="classes/Mongrel/DirHandler.html#M000010">send_file (Mongrel::DirHandler)</a><br />
50
+ <a href="classes/Mongrel/HttpResponse.html#M000025">start (Mongrel::HttpResponse)</a><br />
51
+ <a href="classes/Mongrel/URIClassifier.html#M000021">unregister (Mongrel::URIClassifier)</a><br />
52
+ <a href="classes/Mongrel/HttpServer.html#M000016">unregister (Mongrel::HttpServer)</a><br />
48
53
  </div>
49
54
  </div>
50
55
  </body>
@@ -11,6 +11,7 @@ class SimpleHandler < Mongrel::HttpHandler
11
11
  end
12
12
 
13
13
  h = Mongrel::HttpServer.new("0.0.0.0", "3000")
14
- h.register("/test", SimpleHandler.new)
14
+ h.register("/test;mystuff;thosestuffs", SimpleHandler.new)
15
+ h.register("/files", Mongrel::DirHandler.new("."))
15
16
  h.run.join
16
17
 
@@ -13,7 +13,7 @@ static int id_handler_map;
13
13
 
14
14
  static VALUE global_http_prefix;
15
15
  static VALUE global_request_method;
16
- static VALUE global_path_info;
16
+ static VALUE global_request_uri;
17
17
  static VALUE global_query_string;
18
18
  static VALUE global_http_version;
19
19
 
@@ -45,11 +45,11 @@ void request_method(void *data, const char *at, size_t length)
45
45
  rb_hash_aset(req, global_request_method, val);
46
46
  }
47
47
 
48
- void path_info(void *data, const char *at, size_t length)
48
+ void request_uri(void *data, const char *at, size_t length)
49
49
  {
50
50
  VALUE req = (VALUE)data;
51
51
  VALUE val = rb_str_new(at, length);
52
- rb_hash_aset(req, global_path_info, val);
52
+ rb_hash_aset(req, global_request_uri, val);
53
53
  }
54
54
 
55
55
 
@@ -87,7 +87,7 @@ VALUE HttpParser_alloc(VALUE klass)
87
87
  TRACE();
88
88
  hp->http_field = http_field;
89
89
  hp->request_method = request_method;
90
- hp->path_info = path_info;
90
+ hp->request_uri = request_uri;
91
91
  hp->query_string = query_string;
92
92
  hp->http_version = http_version;
93
93
 
@@ -279,8 +279,9 @@ VALUE URIClassifier_init(VALUE self)
279
279
  *
280
280
  * Registers the SampleHandler (one for all requests) with the "/someuri".
281
281
  * When URIClassifier::resolve is called with "/someuri" it'll return
282
- * SampleHandler immediately. When "/someuri/pathhere" is called it'll
283
- * find SomeHandler after a second search, and setup PATH_INFO="/pathhere".
282
+ * SampleHandler immediately. When called with "/someuri/iwant" it'll also
283
+ * return SomeHandler immediatly, with no additional searches, but it will
284
+ * return path info with "/iwant".
284
285
  *
285
286
  * You actually can reuse this class to register nearly anything and
286
287
  * quickly resolve it. This could be used for caching, fast mapping, etc.
@@ -357,7 +358,7 @@ VALUE URIClassifier_unregister(VALUE self, VALUE uri)
357
358
  * It also means that it's very efficient to do this only taking as long as the URI has
358
359
  * characters.
359
360
  *
360
- * It expects strings. Don't try other string-line stuff yet.
361
+ * It expects strings with no embedded '\0' characters. Don't try other string-line stuff yet.
361
362
  */
362
363
  VALUE URIClassifier_resolve(VALUE self, VALUE uri)
363
364
  {
@@ -401,8 +402,8 @@ void Init_http11()
401
402
  rb_global_variable(&global_http_prefix);
402
403
  global_request_method = rb_str_new2("REQUEST_METHOD");
403
404
  rb_global_variable(&global_request_method);
404
- global_path_info = rb_str_new2("PATH_INFO");
405
- rb_global_variable(&global_path_info);
405
+ global_request_uri = rb_str_new2("REQUEST_URI");
406
+ rb_global_variable(&global_request_uri);
406
407
  global_query_string = rb_str_new2("QUERY_STRING");
407
408
  rb_global_variable(&global_query_string);
408
409
  global_http_version = rb_str_new2("HTTP_VERSION");
@@ -9,7 +9,7 @@
9
9
  #define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
10
10
 
11
11
  /** machine **/
12
- #line 100 "ext/http11/http11_parser.rl"
12
+ #line 98 "ext/http11/http11_parser.rl"
13
13
 
14
14
 
15
15
  /** Data **/
@@ -21,7 +21,7 @@ static int http_parser_first_final = 56;
21
21
 
22
22
  static int http_parser_error = 1;
23
23
 
24
- #line 104 "ext/http11/http11_parser.rl"
24
+ #line 102 "ext/http11/http11_parser.rl"
25
25
 
26
26
  int http_parser_init(http_parser *parser) {
27
27
  int cs = 0;
@@ -30,7 +30,7 @@ int http_parser_init(http_parser *parser) {
30
30
  {
31
31
  cs = http_parser_start;
32
32
  }
33
- #line 108 "ext/http11/http11_parser.rl"
33
+ #line 106 "ext/http11/http11_parser.rl"
34
34
  parser->cs = cs;
35
35
  parser->body_start = NULL;
36
36
  parser->content_len = 0;
@@ -158,8 +158,8 @@ case 9:
158
158
  tr34:
159
159
  #line 33 "ext/http11/http11_parser.rl"
160
160
  {
161
- if(parser->path_info != NULL)
162
- parser->path_info(parser->data, parser->mark, p - parser->mark);
161
+ if(parser->request_uri != NULL)
162
+ parser->request_uri(parser->data, parser->mark, p - parser->mark);
163
163
  }
164
164
  goto st10;
165
165
  tr48:
@@ -601,8 +601,8 @@ case 35:
601
601
  tr46:
602
602
  #line 33 "ext/http11/http11_parser.rl"
603
603
  {
604
- if(parser->path_info != NULL)
605
- parser->path_info(parser->data, parser->mark, p - parser->mark);
604
+ if(parser->request_uri != NULL)
605
+ parser->request_uri(parser->data, parser->mark, p - parser->mark);
606
606
  }
607
607
  goto st36;
608
608
  st36:
@@ -875,7 +875,7 @@ case 55:
875
875
 
876
876
  _out: {}
877
877
  }
878
- #line 127 "ext/http11/http11_parser.rl"
878
+ #line 125 "ext/http11/http11_parser.rl"
879
879
 
880
880
  parser->cs = cs;
881
881
  parser->nread = p - buffer;
@@ -883,7 +883,7 @@ case 55:
883
883
  /* final \r\n combo encountered so stop right here */
884
884
 
885
885
  #line 886 "ext/http11/http11_parser.c"
886
- #line 133 "ext/http11/http11_parser.rl"
886
+ #line 131 "ext/http11/http11_parser.rl"
887
887
  parser->nread++;
888
888
  }
889
889
 
@@ -896,7 +896,7 @@ int http_parser_finish(http_parser *parser)
896
896
 
897
897
 
898
898
  #line 899 "ext/http11/http11_parser.c"
899
- #line 144 "ext/http11/http11_parser.rl"
899
+ #line 142 "ext/http11/http11_parser.rl"
900
900
 
901
901
  parser->cs = cs;
902
902
 
@@ -3,6 +3,10 @@
3
3
 
4
4
  #include <sys/types.h>
5
5
 
6
+ #if defined(_WIN32)
7
+ #include <stddef.h>
8
+ #endif
9
+
6
10
  typedef void (*element_cb)(void *data, const char *at, size_t length);
7
11
  typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);
8
12
 
@@ -19,7 +23,7 @@ typedef struct http_parser {
19
23
 
20
24
  field_cb http_field;
21
25
  element_cb request_method;
22
- element_cb path_info;
26
+ element_cb request_uri;
23
27
  element_cb query_string;
24
28
  element_cb http_version;
25
29
 
@@ -0,0 +1,493 @@
1
+ require 'socket'
2
+ require 'http11'
3
+ require 'thread'
4
+ require 'stringio'
5
+
6
+ # Mongrel module containing all of the classes (include C extensions) for running
7
+ # a Mongrel web server. It contains a minimalist HTTP server with just enough
8
+ # functionality to service web application requests fast as possible.
9
+ module Mongrel
10
+
11
+ # Every standard HTTP code mapped to the appropriate message. These are
12
+ # used so frequently that they are placed directly in Mongrel for easy
13
+ # access rather than Mongrel::Const.
14
+ HTTP_STATUS_CODES = {
15
+ 100 => 'Continue',
16
+ 101 => 'Switching Protocols',
17
+ 200 => 'OK',
18
+ 201 => 'Created',
19
+ 202 => 'Accepted',
20
+ 203 => 'Non-Authoritative Information',
21
+ 204 => 'No Content',
22
+ 205 => 'Reset Content',
23
+ 206 => 'Partial Content',
24
+ 300 => 'Multiple Choices',
25
+ 301 => 'Moved Permanently',
26
+ 302 => 'Moved Temporarily',
27
+ 303 => 'See Other',
28
+ 304 => 'Not Modified',
29
+ 305 => 'Use Proxy',
30
+ 400 => 'Bad Request',
31
+ 401 => 'Unauthorized',
32
+ 402 => 'Payment Required',
33
+ 403 => 'Forbidden',
34
+ 404 => 'Not Found',
35
+ 405 => 'Method Not Allowed',
36
+ 406 => 'Not Acceptable',
37
+ 407 => 'Proxy Authentication Required',
38
+ 408 => 'Request Time-out',
39
+ 409 => 'Conflict',
40
+ 410 => 'Gone',
41
+ 411 => 'Length Required',
42
+ 412 => 'Precondition Failed',
43
+ 413 => 'Request Entity Too Large',
44
+ 414 => 'Request-URI Too Large',
45
+ 415 => 'Unsupported Media Type',
46
+ 500 => 'Internal Server Error',
47
+ 501 => 'Not Implemented',
48
+ 502 => 'Bad Gateway',
49
+ 503 => 'Service Unavailable',
50
+ 504 => 'Gateway Time-out',
51
+ 505 => 'HTTP Version not supported'
52
+ }
53
+
54
+ # Frequently used constants when constructing requests or responses. Many times
55
+ # the constant just refers to a string with the same contents. Using these constants
56
+ # gave about a 3% to 10% performance improvement over using the strings directly.
57
+ # Symbols did not really improve things much compared to constants.
58
+ #
59
+ # While Mongrel does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
60
+ # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
61
+ # too taxing on performance.
62
+ module Const
63
+ # This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this.
64
+ PATH_INFO="PATH_INFO"
65
+ # This is the intial part that your handler is identified as by URIClassifier.
66
+ SCRIPT_NAME="SCRIPT_NAME"
67
+ # The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
68
+ REQUEST_URI='REQUEST_URI'
69
+
70
+ # Content length (also available as HTTP_CONTENT_LENGTH).
71
+ CONTENT_LENGTH='CONTENT_LENGTH'
72
+
73
+ # Content length (also available as CONTENT_LENGTH).
74
+ HTTP_CONTENT_LENGTH='HTTP_CONTENT_LENGTH'
75
+
76
+ # Content type (also available as HTTP_CONTENT_TYPE).
77
+ CONTENT_TYPE='CONTENT_TYPE'
78
+
79
+ # Content type (also available as CONTENT_TYPE).
80
+ HTTP_CONTENT_TYPE='HTTP_CONTENT_TYPE'
81
+
82
+ # Gateway interface key in the HttpRequest parameters.
83
+ GATEWAY_INTERFACE='GATEWAY_INTERFACE'
84
+ # We claim to support CGI/1.2.
85
+ GATEWAY_INTERFACE_VALUE='CGI/1.2'
86
+
87
+ # Hosts remote IP address. Mongrel does not do DNS resolves since that slows
88
+ # processing down considerably.
89
+ REMOTE_ADDR='REMOTE_ADDR'
90
+
91
+ # This is not given since Mongrel does not do DNS resolves. It is only here for
92
+ # completeness for the CGI standard.
93
+ REMOTE_HOST='REMOTE_HOST'
94
+
95
+ # The name/host of our server as given by the HttpServer.new(host,port) call.
96
+ SERVER_NAME='SERVER_NAME'
97
+
98
+ # The port of our server as given by the HttpServer.new(host,port) call.
99
+ SERVER_PORT='SERVER_PORT'
100
+
101
+ # Official server protocol key in the HttpRequest parameters.
102
+ SERVER_PROTOCOL='SERVER_PROTOCOL'
103
+ # Mongrel claims to support HTTP/1.1.
104
+ SERVER_PROTOCOL_VALUE='HTTP/1.1'
105
+
106
+ # The actual server software being used (it's Mongrel man).
107
+ SERVER_SOFTWARE='SERVER_SOFTWARE'
108
+
109
+ # Current Mongrel version (used for SERVER_SOFTWARE and other response headers).
110
+ MONGREL_VERSION='Mongrel 0.2.2'
111
+
112
+ # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
113
+ ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
114
+
115
+ # A common header for indicating the server is too busy. Not used yet.
116
+ ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
117
+
118
+ # The basic max request size we'll try to read.
119
+ CHUNK_SIZE=(16 * 1024)
120
+
121
+ end
122
+
123
+
124
+ # When a handler is found for a registered URI then this class is constructed
125
+ # and passed to your HttpHandler::process method. You should assume that
126
+ # *one* handler processes all requests. Included in the HttpReqeust is a
127
+ # HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
128
+ # which is a string containing the request body (raw for now).
129
+ #
130
+ # Mongrel really only supports small-ish request bodies right now since really
131
+ # huge ones have to be completely read off the wire and put into a string.
132
+ # Later there will be several options for efficiently handling large file
133
+ # uploads.
134
+ class HttpRequest
135
+ attr_reader :body, :params
136
+
137
+ # You don't really call this. It's made for you.
138
+ # Main thing it does is hook up the params, and store any remaining
139
+ # body data into the HttpRequest.body attribute.
140
+ def initialize(params, initial_body, socket)
141
+ @body = initial_body || ""
142
+ @params = params
143
+ @socket = socket
144
+
145
+ # fix up the CGI requirements
146
+ params[Const::CONTENT_LENGTH] = params[Const::HTTP_CONTENT_LENGTH] || 0
147
+ params[Const::CONTENT_TYPE] ||= params[Const::HTTP_CONTENT_TYPE]
148
+
149
+ # now, if the initial_body isn't long enough for the content length we have to fill it
150
+ # TODO: adapt for big ass stuff by writing to a temp file
151
+ clen = params[Const::HTTP_CONTENT_LENGTH].to_i
152
+ if @body.length < clen
153
+ @body << @socket.read(clen - @body.length)
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+ # This class implements a simple way of constructing the HTTP headers dynamically
160
+ # via a Hash syntax. Think of it as a write-only Hash. Refer to HttpResponse for
161
+ # information on how this is used.
162
+ #
163
+ # One consequence of this write-only nature is that you can write multiple headers
164
+ # by just doing them twice (which is sometimes needed in HTTP), but that the normal
165
+ # semantics for Hash (where doing an insert replaces) is not there.
166
+ class HeaderOut
167
+ attr_reader :out
168
+
169
+ def initialize(out)
170
+ @out = out
171
+ end
172
+
173
+ # Simply writes "#{key}: #{value}" to an output buffer.
174
+ def[]=(key,value)
175
+ @out.write(key)
176
+ @out.write(": ")
177
+ @out.write(value)
178
+ @out.write("\r\n")
179
+ end
180
+ end
181
+
182
+ # Writes and controls your response to the client using the HTTP/1.1 specification.
183
+ # You use it by simply doing:
184
+ #
185
+ # response.start(200) do |head,out|
186
+ # head['Content-Type'] = 'text/plain'
187
+ # out.write("hello\n")
188
+ # end
189
+ #
190
+ # The parameter to start is the response code--which Mongrel will translate for you
191
+ # based on HTTP_STATUS_CODES. The head parameter is how you write custom headers.
192
+ # The out parameter is where you write your body. The default status code for
193
+ # HttpResponse.start is 200 so the above example is redundant.
194
+ #
195
+ # As you can see, it's just like using a Hash and as you do this it writes the proper
196
+ # header to the output on the fly. You can even intermix specifying headers and
197
+ # writing content. The HttpResponse class with write the things in the proper order
198
+ # once the HttpResponse.block is ended.
199
+ #
200
+ # You may also work the HttpResponse object directly using the various attributes available
201
+ # for the raw socket, body, header, and status codes. If you do this you're on your own.
202
+ # A design decision was made to force the client to not pipeline requests. HTTP/1.1
203
+ # pipelining really kills the performance due to how it has to be handled and how
204
+ # unclear the standard is. To fix this the HttpResponse gives a "Connection: close"
205
+ # header which forces the client to close right away. The bonus for this is that it
206
+ # gives a pretty nice speed boost to most clients since they can close their connection
207
+ # immediately.
208
+ #
209
+ # One additional caveat is that you don't have to specify the Content-length header
210
+ # as the HttpResponse will write this for you based on the out length.
211
+ class HttpResponse
212
+ attr_reader :socket
213
+ attr_reader :body
214
+ attr_reader :header
215
+ attr_reader :status
216
+ attr_writer :status
217
+
218
+ def initialize(socket)
219
+ @socket = socket
220
+ @body = StringIO.new
221
+ @status = 404
222
+ @header = HeaderOut.new(StringIO.new)
223
+ end
224
+
225
+ # Receives a block passing it the header and body for you to work with.
226
+ # When the block is finished it writes everything you've done to
227
+ # the socket in the proper order. This lets you intermix header and
228
+ # body content as needed.
229
+ def start(status=200)
230
+ @status = status
231
+ yield @header, @body
232
+ finished
233
+ end
234
+
235
+ # Primarily used in exception handling to reset the response output in order to write
236
+ # an alternative response.
237
+ def reset
238
+ @header.out.rewind
239
+ @body.rewind
240
+ end
241
+
242
+ # This takes whatever has been done to header and body and then writes it in the
243
+ # proper format to make an HTTP/1.1 response.
244
+ def finished
245
+ @header.out.rewind
246
+ @body.rewind
247
+
248
+ # connection: close is also added to ensure that the client does not pipeline.
249
+ @socket.write("HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status]}\r\nContent-Length: #{@body.length}\r\nConnection: close\r\n")
250
+ @socket.write(@header.out.read)
251
+ @socket.write("\r\n")
252
+ @socket.write(@body.read)
253
+ end
254
+ end
255
+
256
+
257
+ # You implement your application handler with this. It's very light giving
258
+ # just the minimum necessary for you to handle a request and shoot back
259
+ # a response. Look at the HttpRequest and HttpResponse objects for how
260
+ # to use them.
261
+ class HttpHandler
262
+ def process(request, response)
263
+ end
264
+ end
265
+
266
+
267
+ # This is the main driver of Mongrel, while the Mognrel::HttpParser and Mongrel::URIClassifier
268
+ # make up the majority of how the server functions. It's a very simple class that just
269
+ # has a thread accepting connections and a simple HttpServer.process_client function
270
+ # to do the heavy lifting with the IO and Ruby.
271
+ #
272
+ # You use it by doing the following:
273
+ #
274
+ # server = HttpServer.new("0.0.0.0", 3000)
275
+ # server.register("/stuff", MyNifterHandler.new)
276
+ # server.run.join
277
+ #
278
+ # The last line can be just server.run if you don't want to join the thread used.
279
+ # If you don't though Ruby will mysteriously just exit on you.
280
+ #
281
+ # Ruby's thread implementation is "interesting" to say the least. Experiments with
282
+ # *many* different types of IO processing simply cannot make a dent in it. Future
283
+ # releases of Mongrel will find other creative ways to make threads faster, but don't
284
+ # hold your breath until Ruby 1.9 is actually finally useful.
285
+ class HttpServer
286
+ attr_reader :acceptor
287
+
288
+ # Creates a working server on host:port (strange things happen if port isn't a Number).
289
+ # Use HttpServer::run to start the server.
290
+ #
291
+ # The num_processors variable has varying affects on how requests are processed. You'd
292
+ # think adding more processing threads (processors) would make the server faster, but
293
+ # that's just not true. There's actually an effect of how Ruby does threads such that
294
+ # the more processors waiting on the request queue, the slower the system is to handle
295
+ # each request. But, the lower the number of processors the fewer concurrent responses
296
+ # the server can make.
297
+ #
298
+ # 20 is the default number of processors and is based on experimentation on a few
299
+ # systems. If you find that you overload Mongrel too much
300
+ # try changing it higher. If you find that responses are way too slow
301
+ # try lowering it (after you've tuned your stuff of course).
302
+ # Future versions of Mongrel will make this more dynamic (hopefully).
303
+ def initialize(host, port, num_processors=20)
304
+ @socket = TCPServer.new(host, port)
305
+
306
+ @classifier = URIClassifier.new
307
+ @req_queue = Queue.new
308
+ @host = host
309
+ @port = port
310
+ @num_procesors = num_processors
311
+
312
+ num_processors.times {|i| Thread.new do
313
+ while client = @req_queue.deq
314
+ process_client(client)
315
+ end
316
+ end
317
+ }
318
+ end
319
+
320
+
321
+ # Does the majority of the IO processing. It has been written in Ruby using
322
+ # about 7 different IO processing strategies and no matter how it's done
323
+ # the performance just does not improve. It is currently carefully constructed
324
+ # to make sure that it gets the best possible performance, but anyone who
325
+ # thinks they can make it faster is more than welcome to take a crack at it.
326
+ def process_client(client)
327
+ begin
328
+ parser = HttpParser.new
329
+ params = {}
330
+ data = client.readpartial(Const::CHUNK_SIZE)
331
+
332
+ while true
333
+ nread = parser.execute(params, data)
334
+ if parser.finished?
335
+ script_name, path_info, handler = @classifier.resolve(params[Const::REQUEST_URI])
336
+
337
+ if handler
338
+ params[Const::PATH_INFO] = path_info
339
+ params[Const::SCRIPT_NAME] = script_name
340
+ params[Const::GATEWAY_INTERFACE]=Const::GATEWAY_INTERFACE_VALUE
341
+ params[Const::REMOTE_ADDR]=client.peeraddr
342
+ params[Const::SERVER_NAME]=@host
343
+ params[Const::SERVER_PORT]=@port
344
+ params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE
345
+ params[Const::SERVER_SOFTWARE]=Const::MONGREL_VERSION
346
+
347
+ request = HttpRequest.new(params, data[nread ... data.length], client)
348
+ response = HttpResponse.new(client)
349
+ handler.process(request, response)
350
+ else
351
+ client.write(Const::ERROR_404_RESPONSE)
352
+ end
353
+
354
+ break
355
+ else
356
+ # gotta stream and read again until we can get the parser to be character safe
357
+ # TODO: make this more efficient since this means we're parsing a lot repeatedly
358
+ parser.reset
359
+ data << client.readpartial(Const::CHUNK_SIZE)
360
+ end
361
+ end
362
+ rescue EOFError
363
+ # ignored
364
+ rescue Errno::ECONNRESET
365
+ # ignored
366
+ rescue Errno::EPIPE
367
+ # ignored
368
+ rescue => details
369
+ STDERR.puts "ERROR(#{details.class}): #{details}"
370
+ STDERR.puts details.backtrace.join("\n")
371
+ ensure
372
+ client.close
373
+ end
374
+ end
375
+
376
+ # Runs the thing. It returns the thread used so you can "join" it. You can also
377
+ # access the HttpServer::acceptor attribute to get the thread later.
378
+ def run
379
+ BasicSocket.do_not_reverse_lookup=true
380
+ @acceptor = Thread.new do
381
+ while true
382
+ @req_queue << @socket.accept
383
+ end
384
+ end
385
+ end
386
+
387
+
388
+ # Simply registers a handler with the internal URIClassifier. When the URI is
389
+ # found in the prefix of a request then your handler's HttpHandler::process method
390
+ # is called. See Mongrel::URIClassifier#register for more information.
391
+ def register(uri, handler)
392
+ @classifier.register(uri, handler)
393
+ end
394
+
395
+ # Removes any handler registered at the given URI. See Mongrel::URIClassifier#unregister
396
+ # for more information.
397
+ def unregister(uri)
398
+ @classifier.unregister(uri)
399
+ end
400
+ end
401
+
402
+
403
+ # The server normally returns a 404 response if a URI is requested, but it
404
+ # also returns a lame empty message. This lets you do a 404 response
405
+ # with a custom message for special URIs.
406
+ class Error404Handler < HttpHandler
407
+
408
+ # Sets the message to return. This is constructed once for the handler
409
+ # so it's pretty efficient.
410
+ def initialize(msg)
411
+ @response = HttpServer::ERROR_404_RESPONSE + msg
412
+ end
413
+
414
+ # Just kicks back the standard 404 response with your special message.
415
+ def process(request, response)
416
+ response.socket.write(@response)
417
+ end
418
+
419
+ end
420
+
421
+
422
+ # Serves the contents of a directory. You give it the path to the root
423
+ # where the files are located, and it tries to find the files based on
424
+ # the PATH_INFO inside the directory. If the requested path is a
425
+ # directory then it returns a simple directory listing.
426
+ #
427
+ # It does a simple protection against going outside it's root path by
428
+ # converting all paths to an absolute expanded path, and then making sure
429
+ # that the final expanded path includes the root path. If it doesn't
430
+ # than it simply gives a 404.
431
+ class DirHandler < HttpHandler
432
+
433
+ # You give it the path to the directory root and an (optional)
434
+ def initialize(path, listing_allowed=true)
435
+ @path = File.expand_path(path)
436
+ @listing_allowed=listing_allowed
437
+ puts "DIR: #@path"
438
+ end
439
+
440
+ def send_dir_listing(base, dir, response)
441
+ if @listing_allowed
442
+ response.start(200) do |head,out|
443
+ head['Content-Type'] = "text/html"
444
+ out << "<html><head><title>Directory Listing</title></head><body>"
445
+ Dir.entries(dir).each do |child|
446
+ out << "<a href=\"#{base}/#{child}\">#{child}</a><br/>"
447
+ end
448
+ out << "</body></html>"
449
+ end
450
+ else
451
+ response.start(403) do |head,out|
452
+ out.write("Directory listings not allowed")
453
+ end
454
+ end
455
+ end
456
+
457
+
458
+ def send_file(req, response)
459
+ response.start(200) do |head,out|
460
+ open(req, "r") do |f|
461
+ out.write(f.read)
462
+ end
463
+ end
464
+ end
465
+
466
+
467
+ def process(request, response)
468
+ req = File.expand_path("." + request.params['PATH_INFO'], @path)
469
+ puts "FIND: #{req}"
470
+ if req.index(@path) != 0 or !File.exist? req
471
+ # not found, return a 404
472
+ response.start(404) do |head,out|
473
+ out << "File not found"
474
+ end
475
+ else
476
+ begin
477
+ if File.directory? req
478
+ send_dir_listing(request.params["REQUEST_URI"],req, response)
479
+ else
480
+ send_file(req, response)
481
+ end
482
+ rescue => details
483
+ response.reset
484
+ response.start(403) do |head,out|
485
+ out << "Error accessing file"
486
+ end
487
+ STDERR.puts "ERROR: #{details}"
488
+ end
489
+ end
490
+ end
491
+ end
492
+
493
+ end