mongrel 0.2.2 → 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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