mongrel 0.2.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/README +27 -25
  2. data/Rakefile +2 -3
  3. data/bin/mongrel_rails +114 -0
  4. data/doc/rdoc/classes/Mongrel.html +30 -0
  5. data/doc/rdoc/classes/Mongrel.src/M000001.html +18 -0
  6. data/doc/rdoc/classes/Mongrel/Const.html +2 -2
  7. data/doc/rdoc/classes/Mongrel/DirHandler.html +84 -21
  8. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000009.html +7 -18
  9. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000010.html +26 -9
  10. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000011.html +27 -26
  11. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000012.html +31 -0
  12. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000013.html +38 -0
  13. data/doc/rdoc/classes/Mongrel/Error404Handler.html +10 -10
  14. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000028.html → M000033.html} +4 -4
  15. data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000029.html → M000034.html} +4 -4
  16. data/doc/rdoc/classes/Mongrel/HeaderOut.html +10 -10
  17. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000017.html → M000019.html} +4 -4
  18. data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000018.html → M000020.html} +7 -7
  19. data/doc/rdoc/classes/Mongrel/HttpHandler.html +5 -5
  20. data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000023.html → M000025.html} +3 -3
  21. data/doc/rdoc/classes/Mongrel/HttpParser.html +35 -35
  22. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000002.html +5 -6
  23. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000003.html +7 -7
  24. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000004.html +8 -20
  25. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000005.html +20 -6
  26. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000006.html +5 -5
  27. data/doc/rdoc/classes/Mongrel/HttpParser.src/M000007.html +5 -6
  28. data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000001.html → M000008.html} +6 -6
  29. data/doc/rdoc/classes/Mongrel/HttpRequest.html +5 -5
  30. data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000030.html → M000035.html} +17 -17
  31. data/doc/rdoc/classes/Mongrel/HttpResponse.html +66 -21
  32. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000026.html +8 -6
  33. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000027.html +7 -12
  34. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000028.html +19 -0
  35. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000029.html +18 -0
  36. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000030.html +20 -0
  37. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000031.html +21 -0
  38. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000032.html +20 -0
  39. data/doc/rdoc/classes/Mongrel/HttpServer.html +28 -28
  40. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000014.html +18 -10
  41. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000015.html +51 -5
  42. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000016.html +9 -4
  43. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000017.html +18 -0
  44. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000018.html +18 -0
  45. data/doc/rdoc/classes/Mongrel/URIClassifier.html +31 -21
  46. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000021.html +18 -15
  47. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000022.html +25 -42
  48. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000023.html +36 -0
  49. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000024.html +84 -0
  50. data/doc/rdoc/created.rid +1 -1
  51. data/doc/rdoc/files/README.html +32 -42
  52. data/doc/rdoc/files/ext/http11/http11_c.html +1 -1
  53. data/doc/rdoc/files/lib/mongrel_rb.html +1 -1
  54. data/doc/rdoc/fr_method_index.html +35 -30
  55. data/examples/simpletest.rb +16 -6
  56. data/ext/http11/http11.c +19 -3
  57. data/ext/http11/tst_search.c +2 -3
  58. data/lib/mongrel.rb +108 -20
  59. data/test/test_uriclassifier.rb +22 -1
  60. metadata +25 -19
  61. data/doc/rdoc/classes/Mongrel/DirHandler.src/M000008.html +0 -20
  62. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000024.html +0 -21
  63. data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000025.html +0 -20
  64. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000012.html +0 -31
  65. data/doc/rdoc/classes/Mongrel/HttpServer.src/M000013.html +0 -64
  66. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000019.html +0 -39
  67. data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000020.html +0 -51
  68. data/lib/#mongrel.rb# +0 -493
@@ -1,20 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>new (Mongrel::DirHandler)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre> <span class="ruby-comment cmt"># File lib/mongrel.rb, line 433</span>
14
- 433: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>(<span class="ruby-identifier">path</span>, <span class="ruby-identifier">listing_allowed</span>=<span class="ruby-keyword kw">true</span>)
15
- 434: <span class="ruby-ivar">@path</span> = <span class="ruby-constant">File</span>.<span class="ruby-identifier">expand_path</span>(<span class="ruby-identifier">path</span>)
16
- 435: <span class="ruby-ivar">@listing_allowed</span>=<span class="ruby-identifier">listing_allowed</span>
17
- 436: <span class="ruby-identifier">puts</span> <span class="ruby-value str">&quot;DIR: #@path&quot;</span>
18
- 437: <span class="ruby-keyword kw">end</span></pre>
19
- </body>
20
- </html>
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>new (Mongrel::HttpResponse)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre> <span class="ruby-comment cmt"># File lib/mongrel.rb, line 218</span>
14
- 218: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>(<span class="ruby-identifier">socket</span>)
15
- 219: <span class="ruby-ivar">@socket</span> = <span class="ruby-identifier">socket</span>
16
- 220: <span class="ruby-ivar">@body</span> = <span class="ruby-constant">StringIO</span>.<span class="ruby-identifier">new</span>
17
- 221: <span class="ruby-ivar">@status</span> = <span class="ruby-value">404</span>
18
- 222: <span class="ruby-ivar">@header</span> = <span class="ruby-constant">HeaderOut</span>.<span class="ruby-identifier">new</span>(<span class="ruby-constant">StringIO</span>.<span class="ruby-identifier">new</span>)
19
- 223: <span class="ruby-keyword kw">end</span></pre>
20
- </body>
21
- </html>
@@ -1,20 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>start (Mongrel::HttpResponse)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre> <span class="ruby-comment cmt"># File lib/mongrel.rb, line 229</span>
14
- 229: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">start</span>(<span class="ruby-identifier">status</span>=<span class="ruby-value">200</span>)
15
- 230: <span class="ruby-ivar">@status</span> = <span class="ruby-identifier">status</span>
16
- 231: <span class="ruby-keyword kw">yield</span> <span class="ruby-ivar">@header</span>, <span class="ruby-ivar">@body</span>
17
- 232: <span class="ruby-identifier">finished</span>
18
- 233: <span class="ruby-keyword kw">end</span></pre>
19
- </body>
20
- </html>
@@ -1,31 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>new (Mongrel::HttpServer)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre> <span class="ruby-comment cmt"># File lib/mongrel.rb, line 303</span>
14
- 303: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>(<span class="ruby-identifier">host</span>, <span class="ruby-identifier">port</span>, <span class="ruby-identifier">num_processors</span>=<span class="ruby-value">20</span>)
15
- 304: <span class="ruby-ivar">@socket</span> = <span class="ruby-constant">TCPServer</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">host</span>, <span class="ruby-identifier">port</span>)
16
- 305:
17
- 306: <span class="ruby-ivar">@classifier</span> = <span class="ruby-constant">URIClassifier</span>.<span class="ruby-identifier">new</span>
18
- 307: <span class="ruby-ivar">@req_queue</span> = <span class="ruby-constant">Queue</span>.<span class="ruby-identifier">new</span>
19
- 308: <span class="ruby-ivar">@host</span> = <span class="ruby-identifier">host</span>
20
- 309: <span class="ruby-ivar">@port</span> = <span class="ruby-identifier">port</span>
21
- 310: <span class="ruby-ivar">@num_procesors</span> = <span class="ruby-identifier">num_processors</span>
22
- 311:
23
- 312: <span class="ruby-identifier">num_processors</span>.<span class="ruby-identifier">times</span> {<span class="ruby-operator">|</span><span class="ruby-identifier">i</span><span class="ruby-operator">|</span> <span class="ruby-constant">Thread</span>.<span class="ruby-identifier">new</span> <span class="ruby-keyword kw">do</span>
24
- 313: <span class="ruby-keyword kw">while</span> <span class="ruby-identifier">client</span> = <span class="ruby-ivar">@req_queue</span>.<span class="ruby-identifier">deq</span>
25
- 314: <span class="ruby-identifier">process_client</span>(<span class="ruby-identifier">client</span>)
26
- 315: <span class="ruby-keyword kw">end</span>
27
- 316: <span class="ruby-keyword kw">end</span>
28
- 317: }
29
- 318: <span class="ruby-keyword kw">end</span></pre>
30
- </body>
31
- </html>
@@ -1,64 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>process_client (Mongrel::HttpServer)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre> <span class="ruby-comment cmt"># File lib/mongrel.rb, line 326</span>
14
- 326: <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">process_client</span>(<span class="ruby-identifier">client</span>)
15
- 327: <span class="ruby-keyword kw">begin</span>
16
- 328: <span class="ruby-identifier">parser</span> = <span class="ruby-constant">HttpParser</span>.<span class="ruby-identifier">new</span>
17
- 329: <span class="ruby-identifier">params</span> = {}
18
- 330: <span class="ruby-identifier">data</span> = <span class="ruby-identifier">client</span>.<span class="ruby-identifier">readpartial</span>(<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">CHUNK_SIZE</span>)
19
- 331:
20
- 332: <span class="ruby-keyword kw">while</span> <span class="ruby-keyword kw">true</span>
21
- 333: <span class="ruby-identifier">nread</span> = <span class="ruby-identifier">parser</span>.<span class="ruby-identifier">execute</span>(<span class="ruby-identifier">params</span>, <span class="ruby-identifier">data</span>)
22
- 334: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">parser</span>.<span class="ruby-identifier">finished?</span>
23
- 335: <span class="ruby-identifier">script_name</span>, <span class="ruby-identifier">path_info</span>, <span class="ruby-identifier">handler</span> = <span class="ruby-ivar">@classifier</span>.<span class="ruby-identifier">resolve</span>(<span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">REQUEST_URI</span>])
24
- 336:
25
- 337: <span class="ruby-keyword kw">if</span> <span class="ruby-identifier">handler</span>
26
- 338: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">PATH_INFO</span>] = <span class="ruby-identifier">path_info</span>
27
- 339: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SCRIPT_NAME</span>] = <span class="ruby-identifier">script_name</span>
28
- 340: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">GATEWAY_INTERFACE</span>]=<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">GATEWAY_INTERFACE_VALUE</span>
29
- 341: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">REMOTE_ADDR</span>]=<span class="ruby-identifier">client</span>.<span class="ruby-identifier">peeraddr</span>
30
- 342: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SERVER_NAME</span>]=<span class="ruby-ivar">@host</span>
31
- 343: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SERVER_PORT</span>]=<span class="ruby-ivar">@port</span>
32
- 344: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SERVER_PROTOCOL</span>]=<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SERVER_PROTOCOL_VALUE</span>
33
- 345: <span class="ruby-identifier">params</span>[<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">SERVER_SOFTWARE</span>]=<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">MONGREL_VERSION</span>
34
- 346:
35
- 347: <span class="ruby-identifier">request</span> = <span class="ruby-constant">HttpRequest</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">params</span>, <span class="ruby-identifier">data</span>[<span class="ruby-identifier">nread</span> <span class="ruby-operator">...</span> <span class="ruby-identifier">data</span>.<span class="ruby-identifier">length</span>], <span class="ruby-identifier">client</span>)
36
- 348: <span class="ruby-identifier">response</span> = <span class="ruby-constant">HttpResponse</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">client</span>)
37
- 349: <span class="ruby-identifier">handler</span>.<span class="ruby-identifier">process</span>(<span class="ruby-identifier">request</span>, <span class="ruby-identifier">response</span>)
38
- 350: <span class="ruby-keyword kw">else</span>
39
- 351: <span class="ruby-identifier">client</span>.<span class="ruby-identifier">write</span>(<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">ERROR_404_RESPONSE</span>)
40
- 352: <span class="ruby-keyword kw">end</span>
41
- 353:
42
- 354: <span class="ruby-keyword kw">break</span>
43
- 355: <span class="ruby-keyword kw">else</span>
44
- 356: <span class="ruby-comment cmt"># gotta stream and read again until we can get the parser to be character safe</span>
45
- 357: <span class="ruby-comment cmt"># TODO: make this more efficient since this means we're parsing a lot repeatedly</span>
46
- 358: <span class="ruby-identifier">parser</span>.<span class="ruby-identifier">reset</span>
47
- 359: <span class="ruby-identifier">data</span> <span class="ruby-operator">&lt;&lt;</span> <span class="ruby-identifier">client</span>.<span class="ruby-identifier">readpartial</span>(<span class="ruby-constant">Const</span><span class="ruby-operator">::</span><span class="ruby-constant">CHUNK_SIZE</span>)
48
- 360: <span class="ruby-keyword kw">end</span>
49
- 361: <span class="ruby-keyword kw">end</span>
50
- 362: <span class="ruby-keyword kw">rescue</span> <span class="ruby-constant">EOFError</span>
51
- 363: <span class="ruby-comment cmt"># ignored</span>
52
- 364: <span class="ruby-keyword kw">rescue</span> <span class="ruby-constant">Errno</span><span class="ruby-operator">::</span><span class="ruby-constant">ECONNRESET</span>
53
- 365: <span class="ruby-comment cmt"># ignored</span>
54
- 366: <span class="ruby-keyword kw">rescue</span> <span class="ruby-constant">Errno</span><span class="ruby-operator">::</span><span class="ruby-constant">EPIPE</span>
55
- 367: <span class="ruby-comment cmt"># ignored</span>
56
- 368: <span class="ruby-keyword kw">rescue</span> =<span class="ruby-operator">&gt;</span> <span class="ruby-identifier">details</span>
57
- 369: <span class="ruby-constant">STDERR</span>.<span class="ruby-identifier">puts</span> <span class="ruby-node">&quot;ERROR(#{details.class}): #{details}&quot;</span>
58
- 370: <span class="ruby-constant">STDERR</span>.<span class="ruby-identifier">puts</span> <span class="ruby-identifier">details</span>.<span class="ruby-identifier">backtrace</span>.<span class="ruby-identifier">join</span>(<span class="ruby-value str">&quot;\n&quot;</span>)
59
- 371: <span class="ruby-keyword kw">ensure</span>
60
- 372: <span class="ruby-identifier">client</span>.<span class="ruby-identifier">close</span>
61
- 373: <span class="ruby-keyword kw">end</span>
62
- 374: <span class="ruby-keyword kw">end</span></pre>
63
- </body>
64
- </html>
@@ -1,39 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>new (Mongrel::URIClassifier)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre>/**
14
- * call-seq:
15
- * URIClassifier.new -&gt; URIClassifier
16
- *
17
- * Initializes a new URIClassifier object that you can use to associate URI sequences
18
- * with objects. You can actually use it with any string sequence and any objects,
19
- * but it's mostly used with URIs.
20
- *
21
- * It uses TST from http://www.octavian.org/cs/software.html to build an ternary search
22
- * trie to hold all of the URIs. It uses this to do an initial search for the a URI
23
- * prefix, and then to break the URI into SCRIPT_NAME and PATH_INFO portions. It actually
24
- * will do two searches most of the time in order to find the right handler for the
25
- * registered prefix portion.
26
- *
27
- */
28
- VALUE URIClassifier_init(VALUE self)
29
- {
30
- VALUE hash;
31
-
32
- // we create an internal hash to protect stuff from the GC
33
- hash = rb_hash_new();
34
- rb_ivar_set(self, id_handler_map, hash);
35
-
36
- return self;
37
- }</pre>
38
- </body>
39
- </html>
@@ -1,51 +0,0 @@
1
- <?xml version="1.0" encoding="iso-8859-1"?>
2
- <!DOCTYPE html
3
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
4
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5
-
6
- <html>
7
- <head>
8
- <title>register (Mongrel::URIClassifier)</title>
9
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
10
- <link rel="stylesheet" href="../../.././rdoc-style.css" type="text/css" media="screen" />
11
- </head>
12
- <body class="standalone-code">
13
- <pre>/**
14
- * call-seq:
15
- * uc.register(&quot;/someuri&quot;, SampleHandler.new) -&gt; nil
16
- *
17
- * Registers the SampleHandler (one for all requests) with the &quot;/someuri&quot;.
18
- * When URIClassifier::resolve is called with &quot;/someuri&quot; it'll return
19
- * SampleHandler immediately. When called with &quot;/someuri/iwant&quot; it'll also
20
- * return SomeHandler immediatly, with no additional searches, but it will
21
- * return path info with &quot;/iwant&quot;.
22
- *
23
- * You actually can reuse this class to register nearly anything and
24
- * quickly resolve it. This could be used for caching, fast mapping, etc.
25
- * The downside is it uses much more memory than a Hash, but it can be
26
- * a lot faster. It's main advantage is that it works on prefixes, which
27
- * is damn hard to get right with a Hash.
28
- */
29
- VALUE URIClassifier_register(VALUE self, VALUE uri, VALUE handler)
30
- {
31
- int rc = 0;
32
- void *ptr = NULL;
33
- struct tst *tst = NULL;
34
- DATA_GET(self, struct tst, tst);
35
-
36
- rc = tst_insert((unsigned char *)StringValueCStr(uri), (void *)handler , tst, 0, &amp;ptr);
37
-
38
- if(rc == TST_DUPLICATE_KEY) {
39
- rb_raise(rb_eStandardError, &quot;Handler already registered with that name&quot;);
40
- } else if(rc == TST_ERROR) {
41
- rb_raise(rb_eStandardError, &quot;Memory error registering handler&quot;);
42
- } else if(rc == TST_NULL_KEY) {
43
- rb_raise(rb_eStandardError, &quot;URI was empty&quot;);
44
- }
45
-
46
- rb_hash_aset(rb_ivar_get(self, id_handler_map), uri, handler);
47
-
48
- return Qnil;
49
- }</pre>
50
- </body>
51
- </html>
@@ -1,493 +0,0 @@
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