mongrel 0.3.13.3 → 0.3.13.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +5 -4
- data/bin/mongrel_rails +162 -169
- data/doc/rdoc/classes/IO.src/M000001.html +5 -5
- data/doc/rdoc/classes/IO.src/M000002.html +5 -5
- data/doc/rdoc/classes/Kernel.html +10 -10
- data/doc/rdoc/classes/Kernel.src/M000010.html +19 -0
- data/doc/rdoc/classes/Kernel.src/M000011.html +23 -0
- data/doc/rdoc/classes/Mongrel.html +27 -10
- data/doc/rdoc/classes/Mongrel/CGIWrapper.html +67 -54
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000101.html → M000112.html} +11 -10
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000102.html → M000113.html} +31 -31
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000103.html → M000114.html} +20 -20
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000115.html +32 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000105.html → M000116.html} +11 -11
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000106.html → M000117.html} +4 -4
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000107.html → M000118.html} +4 -4
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000108.html → M000119.html} +4 -4
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/{M000109.html → M000120.html} +5 -5
- data/doc/rdoc/classes/Mongrel/Camping.html +5 -5
- data/doc/rdoc/classes/Mongrel/Camping.src/{M000048.html → M000039.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.html +10 -10
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/{M000049.html → M000040.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/{M000050.html → M000041.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.html +65 -65
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000016.html +24 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000017.html +42 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000018.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000029.html → M000019.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000030.html → M000020.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000031.html → M000021.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000032.html → M000022.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000033.html → M000023.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000034.html → M000024.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/{M000035.html → M000025.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000026.html +11 -11
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000027.html +11 -29
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000028.html +5 -5
- data/doc/rdoc/classes/Mongrel/Command/Registry.html +15 -15
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/{M000039.html → M000029.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/{M000040.html → M000030.html} +10 -6
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000031.html +58 -0
- data/doc/rdoc/classes/Mongrel/Configurator.html +115 -115
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000091.html +27 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000092.html +31 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000093.html +21 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000094.html +20 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000095.html +23 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000115.html → M000096.html} +4 -4
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000116.html → M000097.html} +24 -24
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000117.html → M000098.html} +5 -5
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000099.html +39 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000119.html → M000100.html} +19 -19
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000120.html → M000101.html} +4 -4
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000121.html → M000102.html} +10 -10
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000122.html → M000103.html} +5 -5
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000123.html → M000104.html} +4 -4
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000124.html → M000105.html} +8 -8
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000106.html +22 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/{M000126.html → M000107.html} +4 -4
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000108.html +34 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000109.html +18 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000110.html +23 -14
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000111.html +5 -18
- data/doc/rdoc/classes/Mongrel/Const.html +11 -6
- data/doc/rdoc/classes/Mongrel/DeflateFilter.html +10 -10
- data/doc/rdoc/classes/Mongrel/DeflateFilter.src/{M000099.html → M000121.html} +5 -5
- data/doc/rdoc/classes/Mongrel/DeflateFilter.src/{M000100.html → M000122.html} +14 -14
- data/doc/rdoc/classes/Mongrel/DirHandler.html +31 -31
- data/doc/rdoc/classes/Mongrel/DirHandler.src/{M000058.html → M000049.html} +0 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/{M000059.html → M000050.html} +0 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/{M000060.html → M000051.html} +12 -16
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000052.html +63 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/{M000062.html → M000053.html} +25 -25
- data/doc/rdoc/classes/Mongrel/DirHandler.src/{M000063.html → M000054.html} +4 -4
- data/doc/rdoc/classes/Mongrel/Error404Handler.html +10 -10
- data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000131.html → M000123.html} +0 -0
- data/doc/rdoc/classes/Mongrel/Error404Handler.src/{M000132.html → M000124.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HeaderOut.html +10 -10
- data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000072.html → M000064.html} +4 -4
- data/doc/rdoc/classes/Mongrel/HeaderOut.src/{M000073.html → M000065.html} +4 -4
- data/doc/rdoc/classes/Mongrel/HttpHandler.html +15 -15
- data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000078.html → M000070.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000079.html → M000071.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandler.src/{M000080.html → M000072.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.html +20 -20
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000022.html → M000012.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000023.html → M000013.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000024.html → M000014.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/{M000025.html → M000015.html} +0 -0
- data/doc/rdoc/classes/Mongrel/HttpParams.html +131 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.html +35 -35
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000051.html → M000042.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000052.html → M000043.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000053.html → M000044.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000054.html → M000045.html} +3 -3
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000055.html → M000046.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000056.html → M000047.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpParser.src/{M000057.html → M000048.html} +1 -1
- data/doc/rdoc/classes/Mongrel/HttpRequest.html +63 -24
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000130.html +47 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000131.html +37 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000132.html +29 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000139.html → M000133.html} +6 -6
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000140.html → M000134.html} +6 -6
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/{M000141.html → M000135.html} +18 -18
- data/doc/rdoc/classes/Mongrel/HttpResponse.html +66 -72
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000073.html +25 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000074.html +20 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000075.html +26 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000076.html +22 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000085.html → M000077.html} +8 -8
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000086.html → M000078.html} +8 -8
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000079.html +31 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/{M000088.html → M000080.html} +7 -7
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000081.html +7 -12
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000082.html +7 -7
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000083.html +7 -12
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000084.html +5 -9
- data/doc/rdoc/classes/Mongrel/HttpServer.html +58 -43
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000064.html → M000055.html} +11 -11
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000065.html → M000056.html} +76 -72
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000066.html → M000057.html} +18 -17
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000067.html → M000058.html} +7 -7
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000059.html +22 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000060.html +62 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000069.html → M000061.html} +20 -20
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000070.html → M000062.html} +4 -4
- data/doc/rdoc/classes/Mongrel/HttpServer.src/{M000071.html → M000063.html} +8 -8
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.html +15 -15
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/{M000042.html → M000032.html} +23 -25
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/{M000043.html → M000033.html} +11 -11
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/{M000044.html → M000034.html} +12 -12
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.html +30 -15
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/{M000045.html → M000035.html} +9 -8
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000036.html +54 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000037.html +21 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000038.html +25 -0
- data/doc/rdoc/classes/Mongrel/RedirectHandler.html +10 -10
- data/doc/rdoc/classes/Mongrel/RedirectHandler.src/{M000136.html → M000128.html} +8 -8
- data/doc/rdoc/classes/Mongrel/RedirectHandler.src/{M000137.html → M000129.html} +13 -13
- data/doc/rdoc/classes/Mongrel/StatisticsFilter.html +16 -16
- data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000125.html +24 -0
- data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/{M000134.html → M000126.html} +10 -10
- data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/{M000135.html → M000127.html} +4 -4
- data/doc/rdoc/classes/{Stats.html → Mongrel/Stats.html} +44 -60
- data/doc/rdoc/classes/Mongrel/Stats.src/M000136.html +19 -0
- data/doc/rdoc/classes/Mongrel/Stats.src/M000137.html +23 -0
- data/doc/rdoc/classes/Mongrel/Stats.src/M000138.html +26 -0
- data/doc/rdoc/classes/Mongrel/Stats.src/M000139.html +18 -0
- data/doc/rdoc/classes/{Stats.src/M000009.html → Mongrel/Stats.src/M000140.html} +6 -6
- data/doc/rdoc/classes/Mongrel/Stats.src/M000141.html +18 -0
- data/doc/rdoc/classes/Mongrel/Stats.src/M000142.html +23 -0
- data/doc/rdoc/classes/Mongrel/Stats.src/M000143.html +20 -0
- data/doc/rdoc/classes/Mongrel/StatusHandler.html +20 -20
- data/doc/rdoc/classes/Mongrel/StatusHandler.src/{M000074.html → M000066.html} +4 -4
- data/doc/rdoc/classes/Mongrel/StatusHandler.src/{M000075.html → M000067.html} +10 -10
- data/doc/rdoc/classes/Mongrel/StatusHandler.src/{M000076.html → M000068.html} +28 -28
- data/doc/rdoc/classes/Mongrel/StatusHandler.src/{M000077.html → M000069.html} +6 -6
- data/doc/rdoc/classes/Mongrel/URIClassifier.html +30 -30
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000093.html → M000085.html} +4 -4
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000094.html → M000086.html} +4 -4
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000095.html → M000087.html} +0 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000096.html → M000088.html} +1 -1
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000097.html → M000089.html} +0 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/{M000098.html → M000090.html} +1 -1
- data/doc/rdoc/classes/MongrelDbg.html +26 -26
- data/doc/rdoc/classes/MongrelDbg.src/M000005.html +24 -0
- data/doc/rdoc/classes/MongrelDbg.src/{M000016.html → M000006.html} +6 -6
- data/doc/rdoc/classes/MongrelDbg.src/{M000017.html → M000007.html} +8 -8
- data/doc/rdoc/classes/MongrelDbg.src/{M000018.html → M000008.html} +7 -7
- data/doc/rdoc/classes/MongrelDbg.src/{M000019.html → M000009.html} +4 -4
- data/doc/rdoc/classes/RequestLog/Access.html +5 -5
- data/doc/rdoc/classes/RequestLog/Access.src/{M000142.html → M000144.html} +5 -5
- data/doc/rdoc/classes/RequestLog/Files.html +5 -5
- data/doc/rdoc/classes/RequestLog/Files.src/{M000143.html → M000145.html} +5 -5
- data/doc/rdoc/classes/RequestLog/Objects.html +12 -6
- data/doc/rdoc/classes/RequestLog/Objects.src/M000147.html +51 -0
- data/doc/rdoc/classes/RequestLog/Params.html +5 -5
- data/doc/rdoc/classes/RequestLog/Params.src/{M000146.html → M000148.html} +5 -5
- data/doc/rdoc/classes/RequestLog/Threads.html +5 -5
- data/doc/rdoc/classes/RequestLog/Threads.src/{M000144.html → M000146.html} +20 -20
- data/doc/rdoc/classes/{ObjectTracker.html → Sync.html} +26 -45
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/COPYING.html +1 -1
- data/doc/rdoc/files/LICENSE.html +1 -1
- data/doc/rdoc/files/README.html +1 -1
- data/doc/rdoc/files/ext/http11/http11_c.html +1 -1
- data/doc/rdoc/files/lib/mongrel/camping_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/cgi_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/command_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/configurator_rb.html +111 -0
- data/doc/rdoc/files/lib/mongrel/debug_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/handlers_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/init_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/rails_rb.html +2 -1
- data/doc/rdoc/files/lib/mongrel/stats_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel/tcphack_rb.html +1 -1
- data/doc/rdoc/files/lib/mongrel_rb.html +2 -4
- data/doc/rdoc/fr_class_index.html +3 -2
- data/doc/rdoc/fr_file_index.html +1 -0
- data/doc/rdoc/fr_method_index.html +144 -142
- data/ext/http11/http11.c +69 -52
- data/ext/http11/http11_parser.c +366 -282
- data/ext/http11/http11_parser.h +2 -0
- data/ext/http11/http11_parser.rl +192 -0
- data/lib/mongrel.rb +127 -433
- data/lib/mongrel/cgi.rb +19 -6
- data/lib/mongrel/command.rb +15 -3
- data/lib/mongrel/configurator.rb +374 -0
- data/lib/mongrel/debug.rb +47 -56
- data/lib/mongrel/handlers.rb +16 -20
- data/lib/mongrel/rails.rb +36 -20
- data/lib/mongrel/stats.rb +60 -58
- data/test/test_conditional.rb +37 -30
- data/test/test_http11.rb +14 -14
- data/test/test_stats.rb +2 -2
- data/test/test_ws.rb +4 -2
- data/tools/rakehelp.rb +4 -4
- metadata +262 -260
- data/doc/rdoc/classes/Kernel.src/M000020.html +0 -19
- data/doc/rdoc/classes/Kernel.src/M000021.html +0 -23
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000104.html +0 -27
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000036.html +0 -24
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000037.html +0 -24
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000038.html +0 -18
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000041.html +0 -50
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000112.html +0 -20
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000113.html +0 -20
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000114.html +0 -23
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000118.html +0 -30
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000125.html +0 -25
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000127.html +0 -35
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000128.html +0 -18
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000129.html +0 -33
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000130.html +0 -18
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000061.html +0 -63
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000138.html +0 -56
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000087.html +0 -31
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000089.html +0 -20
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000090.html +0 -20
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000091.html +0 -20
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000092.html +0 -18
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000068.html +0 -50
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000046.html +0 -51
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000047.html +0 -23
- data/doc/rdoc/classes/Mongrel/StatisticsFilter.src/M000133.html +0 -24
- data/doc/rdoc/classes/MongrelDbg.src/M000015.html +0 -19
- data/doc/rdoc/classes/ObjectTracker.src/M000013.html +0 -27
- data/doc/rdoc/classes/ObjectTracker.src/M000014.html +0 -44
- data/doc/rdoc/classes/RequestLog/Objects.src/M000145.html +0 -19
- data/doc/rdoc/classes/Stats.src/M000005.html +0 -19
- data/doc/rdoc/classes/Stats.src/M000006.html +0 -23
- data/doc/rdoc/classes/Stats.src/M000007.html +0 -26
- data/doc/rdoc/classes/Stats.src/M000008.html +0 -18
- data/doc/rdoc/classes/Stats.src/M000010.html +0 -18
- data/doc/rdoc/classes/Stats.src/M000011.html +0 -23
- data/doc/rdoc/classes/Stats.src/M000012.html +0 -20
- data/lib/http11.so +0 -0
data/ext/http11/http11_parser.h
CHANGED
@@ -23,12 +23,14 @@ typedef struct http_parser {
|
|
23
23
|
size_t mark;
|
24
24
|
size_t field_start;
|
25
25
|
size_t field_len;
|
26
|
+
size_t query_start;
|
26
27
|
|
27
28
|
void *data;
|
28
29
|
|
29
30
|
field_cb http_field;
|
30
31
|
element_cb request_method;
|
31
32
|
element_cb request_uri;
|
33
|
+
element_cb request_path;
|
32
34
|
element_cb query_string;
|
33
35
|
element_cb http_version;
|
34
36
|
element_cb header_done;
|
@@ -0,0 +1,192 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2005 Zed A. Shaw
|
3
|
+
* You can redistribute it and/or modify it under the same terms as Ruby.
|
4
|
+
*/
|
5
|
+
#include "http11_parser.h"
|
6
|
+
#include <stdio.h>
|
7
|
+
#include <assert.h>
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <ctype.h>
|
10
|
+
#include <string.h>
|
11
|
+
|
12
|
+
#define LEN(AT, FPC) (FPC - buffer - parser->AT)
|
13
|
+
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
|
14
|
+
#define PTR_TO(F) (buffer + parser->F)
|
15
|
+
|
16
|
+
/** machine **/
|
17
|
+
%%{
|
18
|
+
machine http_parser;
|
19
|
+
|
20
|
+
action mark {MARK(mark, fpc); }
|
21
|
+
|
22
|
+
|
23
|
+
action start_field { MARK(field_start, fpc); }
|
24
|
+
action write_field {
|
25
|
+
parser->field_len = LEN(field_start, fpc);
|
26
|
+
}
|
27
|
+
|
28
|
+
action start_value { MARK(mark, fpc); }
|
29
|
+
action write_value {
|
30
|
+
if(parser->http_field != NULL) {
|
31
|
+
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
|
32
|
+
}
|
33
|
+
}
|
34
|
+
action request_method {
|
35
|
+
if(parser->request_method != NULL)
|
36
|
+
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
37
|
+
}
|
38
|
+
action request_uri {
|
39
|
+
if(parser->request_uri != NULL)
|
40
|
+
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
41
|
+
}
|
42
|
+
|
43
|
+
action start_query {MARK(query_start, fpc); }
|
44
|
+
action query_string {
|
45
|
+
if(parser->query_string != NULL)
|
46
|
+
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
|
47
|
+
}
|
48
|
+
|
49
|
+
action http_version {
|
50
|
+
if(parser->http_version != NULL)
|
51
|
+
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
|
52
|
+
}
|
53
|
+
|
54
|
+
action request_path {
|
55
|
+
if(parser->request_path != NULL)
|
56
|
+
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
|
57
|
+
}
|
58
|
+
|
59
|
+
action done {
|
60
|
+
parser->body_start = fpc - buffer + 1;
|
61
|
+
if(parser->header_done != NULL)
|
62
|
+
parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
|
63
|
+
fbreak;
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
#### HTTP PROTOCOL GRAMMAR
|
68
|
+
# line endings
|
69
|
+
CRLF = "\r\n";
|
70
|
+
|
71
|
+
# character types
|
72
|
+
CTL = (cntrl | 127);
|
73
|
+
safe = ("$" | "-" | "_" | ".");
|
74
|
+
extra = ("!" | "*" | "'" | "(" | ")" | ",");
|
75
|
+
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
|
76
|
+
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
|
77
|
+
national = any -- (alpha | digit | reserved | extra | safe | unsafe);
|
78
|
+
unreserved = (alpha | digit | safe | extra | national);
|
79
|
+
escape = ("%" xdigit xdigit);
|
80
|
+
uchar = (unreserved | escape);
|
81
|
+
pchar = (uchar | ":" | "@" | "&" | "=" | "+");
|
82
|
+
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
|
83
|
+
|
84
|
+
# elements
|
85
|
+
token = (ascii -- (CTL | tspecials));
|
86
|
+
|
87
|
+
# URI schemes and absolute paths
|
88
|
+
scheme = ( alpha | digit | "+" | "-" | "." )* ;
|
89
|
+
absolute_uri = (scheme ":" (uchar | reserved )*);
|
90
|
+
|
91
|
+
path = (pchar+ ( "/" pchar* )*) ;
|
92
|
+
query = ( uchar | reserved )* %query_string ;
|
93
|
+
param = ( pchar | "/" )* ;
|
94
|
+
params = (param ( ";" param )*) ;
|
95
|
+
rel_path = (path? %request_path (";" params)?) ("?" %start_query query)?;
|
96
|
+
absolute_path = ("/"+ rel_path);
|
97
|
+
|
98
|
+
Request_URI = ("*" | absolute_uri | absolute_path) >mark %request_uri;
|
99
|
+
Method = (upper | digit | safe){1,20} >mark %request_method;
|
100
|
+
|
101
|
+
http_number = (digit+ "." digit+) ;
|
102
|
+
HTTP_Version = ("HTTP/" http_number) >mark %http_version ;
|
103
|
+
Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ;
|
104
|
+
|
105
|
+
field_name = (token -- ":")+ >start_field %write_field;
|
106
|
+
|
107
|
+
field_value = any* >start_value %write_value;
|
108
|
+
|
109
|
+
message_header = field_name ":" " "* field_value :> CRLF;
|
110
|
+
|
111
|
+
Request = Request_Line (message_header)* ( CRLF @done);
|
112
|
+
|
113
|
+
main := Request;
|
114
|
+
}%%
|
115
|
+
|
116
|
+
/** Data **/
|
117
|
+
%% write data;
|
118
|
+
|
119
|
+
int http_parser_init(http_parser *parser) {
|
120
|
+
int cs = 0;
|
121
|
+
%% write init;
|
122
|
+
parser->cs = cs;
|
123
|
+
parser->body_start = 0;
|
124
|
+
parser->content_len = 0;
|
125
|
+
parser->mark = 0;
|
126
|
+
parser->nread = 0;
|
127
|
+
parser->field_len = 0;
|
128
|
+
parser->field_start = 0;
|
129
|
+
|
130
|
+
return(1);
|
131
|
+
}
|
132
|
+
|
133
|
+
|
134
|
+
/** exec **/
|
135
|
+
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) {
|
136
|
+
const char *p, *pe;
|
137
|
+
int cs = parser->cs;
|
138
|
+
|
139
|
+
assert(off <= len && "offset past end of buffer");
|
140
|
+
|
141
|
+
p = buffer+off;
|
142
|
+
pe = buffer+len;
|
143
|
+
|
144
|
+
assert(*pe == '\0' && "pointer does not end on NUL");
|
145
|
+
assert(pe - p == len - off && "pointers aren't same distance");
|
146
|
+
|
147
|
+
|
148
|
+
%% write exec;
|
149
|
+
|
150
|
+
parser->cs = cs;
|
151
|
+
parser->nread += p - (buffer + off);
|
152
|
+
|
153
|
+
assert(p <= pe && "buffer overflow after parsing execute");
|
154
|
+
assert(parser->nread <= len && "nread longer than length");
|
155
|
+
assert(parser->body_start <= len && "body starts after buffer end");
|
156
|
+
assert(parser->mark < len && "mark is after buffer end");
|
157
|
+
assert(parser->field_len <= len && "field has length longer than whole buffer");
|
158
|
+
assert(parser->field_start < len && "field starts after buffer end");
|
159
|
+
|
160
|
+
if(parser->body_start) {
|
161
|
+
/* final \r\n combo encountered so stop right here */
|
162
|
+
%%write eof;
|
163
|
+
parser->nread++;
|
164
|
+
}
|
165
|
+
|
166
|
+
return(parser->nread);
|
167
|
+
}
|
168
|
+
|
169
|
+
int http_parser_finish(http_parser *parser)
|
170
|
+
{
|
171
|
+
int cs = parser->cs;
|
172
|
+
|
173
|
+
%%write eof;
|
174
|
+
|
175
|
+
parser->cs = cs;
|
176
|
+
|
177
|
+
if (http_parser_has_error(parser) ) {
|
178
|
+
return -1;
|
179
|
+
} else if (http_parser_is_finished(parser) ) {
|
180
|
+
return 1;
|
181
|
+
} else {
|
182
|
+
return 0;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
int http_parser_has_error(http_parser *parser) {
|
187
|
+
return parser->cs == http_parser_error;
|
188
|
+
}
|
189
|
+
|
190
|
+
int http_parser_is_finished(http_parser *parser) {
|
191
|
+
return parser->cs == http_parser_first_final;
|
192
|
+
}
|
data/lib/mongrel.rb
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
5
|
# for more information.
|
6
6
|
|
7
|
+
$mongrel_debug_client = false
|
8
|
+
|
7
9
|
require 'socket'
|
8
10
|
require 'http11'
|
9
11
|
require 'tempfile'
|
@@ -14,17 +16,11 @@ require 'mongrel/handlers'
|
|
14
16
|
require 'mongrel/command'
|
15
17
|
require 'mongrel/tcphack'
|
16
18
|
require 'yaml'
|
19
|
+
require 'mongrel/configurator'
|
17
20
|
require 'time'
|
18
21
|
require 'rubygems'
|
19
22
|
require 'etc'
|
20
23
|
|
21
|
-
begin
|
22
|
-
require 'sendfile'
|
23
|
-
STDERR.puts "** You have sendfile installed, will use that to serve files."
|
24
|
-
rescue Object
|
25
|
-
# do nothing
|
26
|
-
end
|
27
|
-
|
28
24
|
|
29
25
|
# Mongrel module containing all of the classes (include C extensions) for running
|
30
26
|
# a Mongrel web server. It contains a minimalist HTTP server with just enough
|
@@ -33,7 +29,7 @@ module Mongrel
|
|
33
29
|
|
34
30
|
class URIClassifier
|
35
31
|
attr_reader :handler_map
|
36
|
-
|
32
|
+
|
37
33
|
# Returns the URIs that have been registered with this classifier so far.
|
38
34
|
# The URIs returned should not be modified as this will cause a memory leak.
|
39
35
|
# You can use this to inspect the contents of the URIClassifier.
|
@@ -119,14 +115,15 @@ module Mongrel
|
|
119
115
|
|
120
116
|
# The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
|
121
117
|
REQUEST_URI='REQUEST_URI'.freeze
|
118
|
+
REQUEST_PATH='REQUEST_PATH'.freeze
|
122
119
|
|
123
|
-
MONGREL_VERSION="0.3.13.
|
120
|
+
MONGREL_VERSION="0.3.13.4".freeze
|
124
121
|
|
125
122
|
# TODO: this use of a base for tempfiles needs to be looked at for security problems
|
126
123
|
MONGREL_TMP_BASE="mongrel".freeze
|
127
124
|
|
128
125
|
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
129
|
-
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
|
126
|
+
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: Mongrel #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
|
130
127
|
|
131
128
|
CONTENT_LENGTH="CONTENT_LENGTH".freeze
|
132
129
|
|
@@ -134,7 +131,7 @@ module Mongrel
|
|
134
131
|
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
135
132
|
|
136
133
|
# The basic max request size we'll try to read.
|
137
|
-
CHUNK_SIZE=(
|
134
|
+
CHUNK_SIZE=(16 * 1024)
|
138
135
|
|
139
136
|
# This is the maximum header that is allowed before a client is booted. The parser detects
|
140
137
|
# this, but we'd also like to do this as well.
|
@@ -144,7 +141,7 @@ module Mongrel
|
|
144
141
|
MAX_BODY=MAX_HEADER
|
145
142
|
|
146
143
|
# A frozen format for this is about 15% faster
|
147
|
-
STATUS_FORMAT = "HTTP/1.1 %d %s\r\
|
144
|
+
STATUS_FORMAT = "HTTP/1.1 %d %s\r\nConnection: close\r\n".freeze
|
148
145
|
CONTENT_TYPE = "Content-Type".freeze
|
149
146
|
LAST_MODIFIED = "Last-Modified".freeze
|
150
147
|
ETAG = "ETag".freeze
|
@@ -158,11 +155,15 @@ module Mongrel
|
|
158
155
|
LINE_END="\r\n".freeze
|
159
156
|
REMOTE_ADDR="REMOTE_ADDR".freeze
|
160
157
|
HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
|
161
|
-
|
158
|
+
HTTP_IF_MODIFIED_SINCE="HTTP_IF_MODIFIED_SINCE".freeze
|
162
159
|
HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze
|
163
160
|
REDIRECT = "HTTP/1.1 302 Found\r\nLocation: %s\r\nConnection: close\r\n\r\n".freeze
|
164
161
|
end
|
165
162
|
|
163
|
+
# Basically a Hash with one extra parameter for the HTTP body, mostly used internally.
|
164
|
+
class HttpParams < Hash
|
165
|
+
attr_accessor :http_body
|
166
|
+
end
|
166
167
|
|
167
168
|
# When a handler is found for a registered URI then this class is constructed
|
168
169
|
# and passed to your HttpHandler::process method. You should assume that
|
@@ -186,48 +187,82 @@ module Mongrel
|
|
186
187
|
# body data into the HttpRequest.body attribute.
|
187
188
|
#
|
188
189
|
# TODO: Implement tempfile removal when the request is done.
|
189
|
-
def initialize(params,
|
190
|
+
def initialize(params, socket, dispatcher)
|
190
191
|
@params = params
|
191
192
|
@socket = socket
|
193
|
+
content_length = params[Const::CONTENT_LENGTH].to_i
|
194
|
+
remain = content_length - params.http_body.length
|
195
|
+
|
192
196
|
|
193
|
-
|
194
|
-
total = clen
|
197
|
+
dispatcher.request_begins(params) if dispatcher
|
195
198
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
else
|
199
|
+
# Some clients (like FF1.0) report 0 for body and then send a body. This will probably truncate them but at least the request goes through usually.
|
200
|
+
if remain <= 0
|
201
|
+
# we've got everything, pack it up
|
200
202
|
@body = StringIO.new
|
203
|
+
@body.write params.http_body
|
204
|
+
dispatcher.request_progress(params, 0, content_length) if dispatcher
|
205
|
+
elsif remain > 0
|
206
|
+
# must read more data to complete body
|
207
|
+
if remain > Const::MAX_BODY
|
208
|
+
# huge body, put it in a tempfile
|
209
|
+
@body = Tempfile.new(Const::MONGREL_TMP_BASE)
|
210
|
+
@body.binmode
|
211
|
+
else
|
212
|
+
# small body, just use that
|
213
|
+
@body = StringIO.new
|
214
|
+
end
|
215
|
+
|
216
|
+
@body.write params.http_body
|
217
|
+
read_body(remain, content_length, dispatcher)
|
201
218
|
end
|
202
219
|
|
203
|
-
|
204
|
-
|
205
|
-
|
220
|
+
@body.rewind if body
|
221
|
+
end
|
222
|
+
|
206
223
|
|
224
|
+
# Does the heavy lifting of properly reading the larger body requests in
|
225
|
+
# small chunks. It expects @body to be an IO object, @socket to be valid,
|
226
|
+
# and will set @body = nil if the request fails. It also expects any initial
|
227
|
+
# part of the body that has been read to be in the @body already.
|
228
|
+
def read_body(remain, total, dispatcher)
|
229
|
+
begin
|
207
230
|
# write the odd sized chunk first
|
208
|
-
|
209
|
-
|
231
|
+
|
232
|
+
remain -= @body.write(read_socket(remain % Const::CHUNK_SIZE))
|
233
|
+
dispatcher.request_progress(params, remain, total) if dispatcher
|
210
234
|
|
211
235
|
# then stream out nothing but perfectly sized chunks
|
212
|
-
|
213
|
-
|
214
|
-
# have to do it this way since @socket.eof? causes it to block
|
215
|
-
raise "Socket closed or read failure" if not data or data.length != Const::CHUNK_SIZE
|
216
|
-
clen -= @body.write(data)
|
236
|
+
until remain <= 0 or @socket.closed?
|
237
|
+
remain -= @body.write(read_socket(Const::CHUNK_SIZE))
|
217
238
|
# ASSUME: we are writing to a disk and these writes always write the requested amount
|
218
|
-
|
239
|
+
dispatcher.request_progress(params, remain, total) if dispatcher
|
219
240
|
end
|
220
|
-
|
221
|
-
# rewind to keep the world happy
|
222
|
-
@body.rewind
|
223
241
|
rescue Object
|
242
|
+
STDERR.puts "ERROR reading http body: #$!"
|
243
|
+
$!.backtrace.join("\n")
|
224
244
|
# any errors means we should delete the file, including if the file is dumped
|
225
|
-
@socket.close
|
245
|
+
@socket.close rescue Object
|
226
246
|
@body.delete if @body.class == Tempfile
|
227
247
|
@body = nil # signals that there was a problem
|
228
248
|
end
|
229
249
|
end
|
230
250
|
|
251
|
+
def read_socket(len)
|
252
|
+
if !@socket.closed?
|
253
|
+
data = @socket.read(len)
|
254
|
+
if !data
|
255
|
+
raise "Socket read return nil"
|
256
|
+
elsif data.length != len
|
257
|
+
raise "Socket read returned insufficient data: #{data.length}"
|
258
|
+
else
|
259
|
+
data
|
260
|
+
end
|
261
|
+
else
|
262
|
+
raise "Socket already closed when reading."
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
231
266
|
# Performs URI escaping so that you can construct proper
|
232
267
|
# query strings faster. Use this rather than the cgi.rb
|
233
268
|
# version since it's faster. (Stolen from Camping).
|
@@ -363,15 +398,16 @@ module Mongrel
|
|
363
398
|
elsif @header_sent
|
364
399
|
raise "You have already sent the request headers."
|
365
400
|
else
|
366
|
-
@header.out.
|
367
|
-
@body.
|
401
|
+
@header.out.truncate(0)
|
402
|
+
@body.close
|
403
|
+
@body = StringIO.new
|
368
404
|
end
|
369
405
|
end
|
370
406
|
|
371
|
-
def send_status(content_length
|
407
|
+
def send_status(content_length=@body.length)
|
372
408
|
if not @status_sent
|
373
|
-
content_length
|
374
|
-
write(Const::STATUS_FORMAT % [status, HTTP_STATUS_CODES[@status]
|
409
|
+
@header['Content-Length'] = content_length unless @status == 304
|
410
|
+
write(Const::STATUS_FORMAT % [@status, HTTP_STATUS_CODES[@status]])
|
375
411
|
@status_sent = true
|
376
412
|
end
|
377
413
|
end
|
@@ -393,33 +429,29 @@ module Mongrel
|
|
393
429
|
end
|
394
430
|
|
395
431
|
# Appends the contents of +path+ to the response stream. The file is opened for binary
|
396
|
-
# reading and written in chunks to the socket.
|
397
|
-
# <a href="http://rubyforge.org/projects/ruby-sendfile">sendfile</a> library is found,
|
398
|
-
# it is used to send the file, often with greater speed and less memory/cpu usage.
|
432
|
+
# reading and written in chunks to the socket.
|
399
433
|
#
|
400
|
-
#
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
begin
|
407
|
-
@socket.sendfile(f)
|
408
|
-
rescue => details
|
409
|
-
socket_error(details)
|
410
|
-
end
|
411
|
-
else
|
434
|
+
# Sendfile API support has been removed in 0.3.13.4 due to stability problems.
|
435
|
+
def send_file(path, small_file = false)
|
436
|
+
if small_file
|
437
|
+
File.open(path, "rb") {|f| @socket << f.read }
|
438
|
+
else
|
439
|
+
File.open(path, "rb") do |f|
|
412
440
|
while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
|
413
|
-
|
441
|
+
begin
|
442
|
+
write(chunk)
|
443
|
+
rescue Object => exc
|
444
|
+
break
|
445
|
+
end
|
414
446
|
end
|
415
447
|
end
|
416
|
-
@body_sent = true
|
417
448
|
end
|
449
|
+
@body_sent = true
|
418
450
|
end
|
419
451
|
|
420
452
|
def socket_error(details)
|
421
453
|
# ignore these since it means the client closed off early
|
422
|
-
@socket.close
|
454
|
+
@socket.close rescue Object
|
423
455
|
done = true
|
424
456
|
raise details
|
425
457
|
end
|
@@ -514,7 +546,7 @@ module Mongrel
|
|
514
546
|
def process_client(client)
|
515
547
|
begin
|
516
548
|
parser = HttpParser.new
|
517
|
-
params =
|
549
|
+
params = HttpParams.new
|
518
550
|
request = nil
|
519
551
|
data = client.readpartial(Const::CHUNK_SIZE)
|
520
552
|
nparsed = 0
|
@@ -527,7 +559,7 @@ module Mongrel
|
|
527
559
|
nparsed = parser.execute(params, data, nparsed)
|
528
560
|
|
529
561
|
if parser.finished?
|
530
|
-
script_name, path_info, handlers = @classifier.resolve(params[Const::
|
562
|
+
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
|
531
563
|
|
532
564
|
if handlers
|
533
565
|
params[Const::PATH_INFO] = path_info
|
@@ -535,10 +567,7 @@ module Mongrel
|
|
535
567
|
params[Const::REMOTE_ADDR] = params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last
|
536
568
|
notifier = handlers[0].request_notify ? handlers[0] : nil
|
537
569
|
|
538
|
-
|
539
|
-
data = data[nparsed ... data.length] || ""
|
540
|
-
|
541
|
-
request = HttpRequest.new(params, data, client, notifier)
|
570
|
+
request = HttpRequest.new(params, client, notifier)
|
542
571
|
|
543
572
|
# in the case of large file uploads the user could close the socket, so skip those requests
|
544
573
|
break if request.body == nil # nil signals from HttpRequest::initialize that the request was aborted
|
@@ -564,22 +593,29 @@ module Mongrel
|
|
564
593
|
break #done
|
565
594
|
else
|
566
595
|
# Parser is not done, queue up more data to read and continue parsing
|
567
|
-
|
596
|
+
chunk = client.readpartial(Const::CHUNK_SIZE)
|
597
|
+
break if !chunk or chunk.length == 0 # read failed, stop processing
|
598
|
+
|
599
|
+
data << chunk
|
568
600
|
if data.length >= Const::MAX_HEADER
|
569
601
|
raise HttpParserError.new("HEADER is longer than allowed, aborting client early.")
|
570
602
|
end
|
571
603
|
end
|
572
604
|
end
|
573
605
|
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
574
|
-
|
606
|
+
client.close rescue Object
|
575
607
|
rescue HttpParserError
|
576
|
-
|
608
|
+
if $mongrel_debug_client
|
609
|
+
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
610
|
+
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
|
611
|
+
end
|
577
612
|
rescue Errno::EMFILE
|
578
613
|
reap_dead_workers('too many files')
|
579
614
|
rescue Object
|
580
615
|
STDERR.puts "#{Time.now}: ERROR: #$!"
|
616
|
+
STDERR.puts $!.backtrace.join("\n") if $mongrel_debug_client
|
581
617
|
ensure
|
582
|
-
client.close
|
618
|
+
client.close rescue Object
|
583
619
|
request.body.delete if request and request.body.class == Tempfile
|
584
620
|
end
|
585
621
|
end
|
@@ -591,13 +627,14 @@ module Mongrel
|
|
591
627
|
def reap_dead_workers(reason='unknown')
|
592
628
|
if @workers.list.length > 0
|
593
629
|
STDERR.puts "#{Time.now}: Reaping #{@workers.list.length} threads for slow workers because of '#{reason}'"
|
630
|
+
error_msg = "Mongrel timed out this thread: #{reason}"
|
594
631
|
mark = Time.now
|
595
632
|
@workers.list.each do |w|
|
596
633
|
w[:started_on] = Time.now if not w[:started_on]
|
597
634
|
|
598
635
|
if mark - w[:started_on] > @death_time + @timeout
|
599
636
|
STDERR.puts "Thread #{w.inspect} is too old, killing."
|
600
|
-
w.raise(TimeoutError.new(
|
637
|
+
w.raise(TimeoutError.new(error_msg))
|
601
638
|
end
|
602
639
|
end
|
603
640
|
end
|
@@ -616,21 +653,34 @@ module Mongrel
|
|
616
653
|
end
|
617
654
|
end
|
618
655
|
|
656
|
+
def configure_socket_options
|
657
|
+
if /linux/ === RUBY_PLATFORM
|
658
|
+
# 9 is currently TCP_DEFER_ACCEPT
|
659
|
+
$tcp_defer_accept_opts = [9,1]
|
660
|
+
$tcp_cork_opts = [3,1]
|
661
|
+
end
|
662
|
+
end
|
619
663
|
|
620
664
|
# Runs the thing. It returns the thread used so you can "join" it. You can also
|
621
665
|
# access the HttpServer::acceptor attribute to get the thread later.
|
622
666
|
def run
|
623
667
|
BasicSocket.do_not_reverse_lookup=true
|
624
668
|
|
669
|
+
configure_socket_options
|
670
|
+
|
671
|
+
@socket.setsockopt(Socket::SOL_TCP, $tcp_defer_accept_opts[0], $tcp_defer_accept_opts[1]) if $tcp_defer_accept_opts
|
672
|
+
|
625
673
|
@acceptor = Thread.new do
|
626
674
|
while true
|
627
675
|
begin
|
628
676
|
client = @socket.accept
|
677
|
+
client.setsockopt(Socket::SOL_TCP, $tcp_cork_opts[0], $tcp_cork_opts[1]) if $tcp_cork_opts
|
678
|
+
|
629
679
|
worker_list = @workers.list
|
630
680
|
|
631
681
|
if worker_list.length >= @num_processors
|
632
682
|
STDERR.puts "Server overloaded with #{worker_list.length} processors (#@num_processors max). Dropping connection."
|
633
|
-
client.close
|
683
|
+
client.close rescue Object
|
634
684
|
reap_dead_workers("max processors")
|
635
685
|
else
|
636
686
|
thread = Thread.new { process_client(client) }
|
@@ -641,11 +691,17 @@ module Mongrel
|
|
641
691
|
sleep @timeout/100 if @timeout > 0
|
642
692
|
end
|
643
693
|
rescue StopServer
|
644
|
-
@socket.close
|
694
|
+
@socket.close rescue Object
|
645
695
|
break
|
646
696
|
rescue Errno::EMFILE
|
647
697
|
reap_dead_workers("too many open files")
|
648
698
|
sleep 0.5
|
699
|
+
rescue Errno::ECONNABORTED
|
700
|
+
# client closed the socket even before accept
|
701
|
+
client.close rescue Object
|
702
|
+
rescue Object => exc
|
703
|
+
STDERR.puts "!!!!!! UNHANDLED EXCEPTION! #{exc}. TELL ZED HE'S A MORON."
|
704
|
+
STDERR.puts $!.backtrace.join("\n") if $mongrel_debug_client
|
649
705
|
end
|
650
706
|
end
|
651
707
|
|
@@ -700,366 +756,4 @@ module Mongrel
|
|
700
756
|
end
|
701
757
|
|
702
758
|
end
|
703
|
-
|
704
|
-
|
705
|
-
# Implements a simple DSL for configuring a Mongrel server for your
|
706
|
-
# purposes. More used by framework implementers to setup Mongrel
|
707
|
-
# how they like, but could be used by regular folks to add more things
|
708
|
-
# to an existing mongrel configuration.
|
709
|
-
#
|
710
|
-
# It is used like this:
|
711
|
-
#
|
712
|
-
# require 'mongrel'
|
713
|
-
# config = Mongrel::Configurator.new :host => "127.0.0.1" do
|
714
|
-
# listener :port => 3000 do
|
715
|
-
# uri "/app", :handler => Mongrel::DirHandler.new(".", load_mime_map("mime.yaml"))
|
716
|
-
# end
|
717
|
-
# run
|
718
|
-
# end
|
719
|
-
#
|
720
|
-
# This will setup a simple DirHandler at the current directory and load additional
|
721
|
-
# mime types from mimy.yaml. The :host => "127.0.0.1" is actually not
|
722
|
-
# specific to the servers but just a hash of default parameters that all
|
723
|
-
# server or uri calls receive.
|
724
|
-
#
|
725
|
-
# When you are inside the block after Mongrel::Configurator.new you can simply
|
726
|
-
# call functions that are part of Configurator (like server, uri, daemonize, etc)
|
727
|
-
# without having to refer to anything else. You can also call these functions on
|
728
|
-
# the resulting object directly for additional configuration.
|
729
|
-
#
|
730
|
-
# A major thing about Configurator is that it actually lets you configure
|
731
|
-
# multiple listeners for any hosts and ports you want. These are kept in a
|
732
|
-
# map config.listeners so you can get to them.
|
733
|
-
#
|
734
|
-
# * :pid_file => Where to write the process ID.
|
735
|
-
class Configurator
|
736
|
-
attr_reader :listeners
|
737
|
-
attr_reader :defaults
|
738
|
-
attr_reader :needs_restart
|
739
|
-
|
740
|
-
# You pass in initial defaults and then a block to continue configuring.
|
741
|
-
def initialize(defaults={}, &blk)
|
742
|
-
@listener = nil
|
743
|
-
@listener_name = nil
|
744
|
-
@listeners = {}
|
745
|
-
@defaults = defaults
|
746
|
-
@needs_restart = false
|
747
|
-
@pid_file = defaults[:pid_file]
|
748
|
-
|
749
|
-
if blk
|
750
|
-
cloaker(&blk).bind(self).call
|
751
|
-
end
|
752
|
-
end
|
753
|
-
|
754
|
-
# Change privilege of the process to specified user and group.
|
755
|
-
def change_privilege(user, group)
|
756
|
-
begin
|
757
|
-
if group
|
758
|
-
log "Changing group to #{group}."
|
759
|
-
Process::GID.change_privilege(Etc.getgrnam(group).gid)
|
760
|
-
end
|
761
|
-
|
762
|
-
if user
|
763
|
-
log "Changing user to #{user}."
|
764
|
-
Process::UID.change_privilege(Etc.getpwnam(user).uid)
|
765
|
-
end
|
766
|
-
rescue Errno::EPERM
|
767
|
-
log "FAILED to change user:group #{user}:#{group}: #$!"
|
768
|
-
exit 1
|
769
|
-
end
|
770
|
-
end
|
771
|
-
|
772
|
-
# Writes the PID file but only if we're on windows.
|
773
|
-
def write_pid_file
|
774
|
-
if RUBY_PLATFORM !~ /mswin/
|
775
|
-
open(@pid_file,"w") {|f| f.write(Process.pid) }
|
776
|
-
end
|
777
|
-
end
|
778
|
-
|
779
|
-
# generates a class for cloaking the current self and making the DSL nicer
|
780
|
-
def cloaking_class
|
781
|
-
class << self
|
782
|
-
self
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
|
-
# Do not call this. You were warned.
|
787
|
-
def cloaker(&blk)
|
788
|
-
cloaking_class.class_eval do
|
789
|
-
define_method :cloaker_, &blk
|
790
|
-
meth = instance_method( :cloaker_ )
|
791
|
-
remove_method :cloaker_
|
792
|
-
meth
|
793
|
-
end
|
794
|
-
end
|
795
|
-
|
796
|
-
# This will resolve the given options against the defaults.
|
797
|
-
# Normally just used internally.
|
798
|
-
def resolve_defaults(options)
|
799
|
-
options.merge(@defaults)
|
800
|
-
end
|
801
|
-
|
802
|
-
# Starts a listener block. This is the only one that actually takes
|
803
|
-
# a block and then you make Configurator.uri calls in order to setup
|
804
|
-
# your URIs and handlers. If you write your Handlers as GemPlugins
|
805
|
-
# then you can use load_plugins and plugin to load them.
|
806
|
-
#
|
807
|
-
# It expects the following options (or defaults):
|
808
|
-
#
|
809
|
-
# * :host => Host name to bind.
|
810
|
-
# * :port => Port to bind.
|
811
|
-
# * :num_processors => The maximum number of concurrent threads allowed. (950 default)
|
812
|
-
# * :timeout => 1/100th of a second timeout between requests. (10 is 1/10th, 0 is timeout)
|
813
|
-
# * :user => User to change to, must have :group as well.
|
814
|
-
# * :group => Group to change to, must have :user as well.
|
815
|
-
#
|
816
|
-
def listener(options={},&blk)
|
817
|
-
raise "Cannot call listener inside another listener block." if (@listener or @listener_name)
|
818
|
-
ops = resolve_defaults(options)
|
819
|
-
ops[:num_processors] ||= 950
|
820
|
-
ops[:timeout] ||= 0
|
821
|
-
|
822
|
-
@listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:timeout].to_i)
|
823
|
-
@listener_name = "#{ops[:host]}:#{ops[:port]}"
|
824
|
-
@listeners[@listener_name] = @listener
|
825
|
-
|
826
|
-
if ops[:user] and ops[:group]
|
827
|
-
change_privilege(ops[:user], ops[:group])
|
828
|
-
end
|
829
|
-
|
830
|
-
# Does the actual cloaking operation to give the new implicit self.
|
831
|
-
if blk
|
832
|
-
cloaker(&blk).bind(self).call
|
833
|
-
end
|
834
|
-
|
835
|
-
# all done processing this listener setup, reset implicit variables
|
836
|
-
@listener = nil
|
837
|
-
@listener_name = nil
|
838
|
-
end
|
839
|
-
|
840
|
-
|
841
|
-
# Called inside a Configurator.listener block in order to
|
842
|
-
# add URI->handler mappings for that listener. Use this as
|
843
|
-
# many times as you like. It expects the following options
|
844
|
-
# or defaults:
|
845
|
-
#
|
846
|
-
# * :handler => HttpHandler -- Handler to use for this location.
|
847
|
-
# * :in_front => true/false -- Rather than appending, it prepends this handler.
|
848
|
-
def uri(location, options={})
|
849
|
-
ops = resolve_defaults(options)
|
850
|
-
@listener.register(location, ops[:handler], in_front=ops[:in_front])
|
851
|
-
end
|
852
|
-
|
853
|
-
|
854
|
-
# Daemonizes the current Ruby script turning all the
|
855
|
-
# listeners into an actual "server" or detached process.
|
856
|
-
# You must call this *before* frameworks that open files
|
857
|
-
# as otherwise the files will be closed by this function.
|
858
|
-
#
|
859
|
-
# Does not work for Win32 systems (the call is silently ignored).
|
860
|
-
#
|
861
|
-
# Requires the following options or defaults:
|
862
|
-
#
|
863
|
-
# * :cwd => Directory to change to.
|
864
|
-
# * :log_file => Where to write STDOUT and STDERR.
|
865
|
-
#
|
866
|
-
# It is safe to call this on win32 as it will only require the daemons
|
867
|
-
# gem/library if NOT win32.
|
868
|
-
def daemonize(options={})
|
869
|
-
ops = resolve_defaults(options)
|
870
|
-
# save this for later since daemonize will hose it
|
871
|
-
if RUBY_PLATFORM !~ /mswin/
|
872
|
-
require 'daemons/daemonize'
|
873
|
-
|
874
|
-
Daemonize.daemonize(log_file=File.join(ops[:cwd], ops[:log_file]))
|
875
|
-
|
876
|
-
# change back to the original starting directory
|
877
|
-
Dir.chdir(ops[:cwd])
|
878
|
-
|
879
|
-
else
|
880
|
-
log "WARNING: Win32 does not support daemon mode."
|
881
|
-
end
|
882
|
-
end
|
883
|
-
|
884
|
-
|
885
|
-
# Uses the GemPlugin system to easily load plugins based on their
|
886
|
-
# gem dependencies. You pass in either an :includes => [] or
|
887
|
-
# :excludes => [] setting listing the names of plugins to include
|
888
|
-
# or exclude from the when determining the dependencies.
|
889
|
-
def load_plugins(options={})
|
890
|
-
ops = resolve_defaults(options)
|
891
|
-
|
892
|
-
load_settings = {}
|
893
|
-
if ops[:includes]
|
894
|
-
ops[:includes].each do |plugin|
|
895
|
-
load_settings[plugin] = GemPlugin::INCLUDE
|
896
|
-
end
|
897
|
-
end
|
898
|
-
|
899
|
-
if ops[:excludes]
|
900
|
-
ops[:excludes].each do |plugin|
|
901
|
-
load_settings[plugin] = GemPlugin::EXCLUDE
|
902
|
-
end
|
903
|
-
end
|
904
|
-
|
905
|
-
GemPlugin::Manager.instance.load(load_settings)
|
906
|
-
end
|
907
|
-
|
908
|
-
|
909
|
-
# Easy way to load a YAML file and apply default settings.
|
910
|
-
def load_yaml(file, default={})
|
911
|
-
default.merge(YAML.load_file(file))
|
912
|
-
end
|
913
|
-
|
914
|
-
|
915
|
-
# Loads the MIME map file and checks that it is correct
|
916
|
-
# on loading. This is commonly passed to Mongrel::DirHandler
|
917
|
-
# or any framework handler that uses DirHandler to serve files.
|
918
|
-
# You can also include a set of default MIME types as additional
|
919
|
-
# settings. See Mongrel::DirHandler for how the MIME types map
|
920
|
-
# is organized.
|
921
|
-
def load_mime_map(file, mime={})
|
922
|
-
# configure any requested mime map
|
923
|
-
mime = load_yaml(file, mime)
|
924
|
-
|
925
|
-
# check all the mime types to make sure they are the right format
|
926
|
-
mime.each {|k,v| log "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
|
927
|
-
|
928
|
-
return mime
|
929
|
-
end
|
930
|
-
|
931
|
-
|
932
|
-
# Loads and creates a plugin for you based on the given
|
933
|
-
# name and configured with the selected options. The options
|
934
|
-
# are merged with the defaults prior to passing them in.
|
935
|
-
def plugin(name, options={})
|
936
|
-
ops = resolve_defaults(options)
|
937
|
-
GemPlugin::Manager.instance.create(name, ops)
|
938
|
-
end
|
939
|
-
|
940
|
-
# Let's you do redirects easily as described in Mongrel::RedirectHandler.
|
941
|
-
# You use it inside the configurator like this:
|
942
|
-
#
|
943
|
-
# redirect("/test", "/to/there") # simple
|
944
|
-
# redirect("/to", /t/, 'w') # regexp
|
945
|
-
# redirect("/hey", /(w+)/) {|match| ...} # block
|
946
|
-
#
|
947
|
-
def redirect(from, pattern, replacement = nil, &block)
|
948
|
-
uri from, :handler => Mongrel::RedirectHandler.new(pattern, replacement, &block)
|
949
|
-
end
|
950
|
-
|
951
|
-
# Works like a meta run method which goes through all the
|
952
|
-
# configured listeners. Use the Configurator.join method
|
953
|
-
# to prevent Ruby from exiting until each one is done.
|
954
|
-
def run
|
955
|
-
@listeners.each {|name,s|
|
956
|
-
s.run
|
957
|
-
}
|
958
|
-
|
959
|
-
$mongrel_sleeper_thread = Thread.new { loop { sleep 1 } }
|
960
|
-
end
|
961
|
-
|
962
|
-
# Calls .stop on all the configured listeners so they
|
963
|
-
# stop processing requests (gracefully). By default it
|
964
|
-
# assumes that you don't want to restart and that the pid file
|
965
|
-
# should be unlinked on exit.
|
966
|
-
def stop(needs_restart=false, unlink_pid_file=true)
|
967
|
-
@listeners.each {|name,s|
|
968
|
-
s.stop
|
969
|
-
}
|
970
|
-
|
971
|
-
@needs_restart = needs_restart
|
972
|
-
if unlink_pid_file
|
973
|
-
File.unlink @pid_file if (@pid_file and File.exist?(@pid_file))
|
974
|
-
end
|
975
|
-
end
|
976
|
-
|
977
|
-
|
978
|
-
# This method should actually be called *outside* of the
|
979
|
-
# Configurator block so that you can control it. In other words
|
980
|
-
# do it like: config.join.
|
981
|
-
def join
|
982
|
-
@listeners.values.each {|s| s.acceptor.join }
|
983
|
-
end
|
984
|
-
|
985
|
-
|
986
|
-
# Calling this before you register your URIs to the given location
|
987
|
-
# will setup a set of handlers that log open files, objects, and the
|
988
|
-
# parameters for each request. This helps you track common problems
|
989
|
-
# found in Rails applications that are either slow or become unresponsive
|
990
|
-
# after a little while.
|
991
|
-
#
|
992
|
-
# You can pass an extra parameter *what* to indicate what you want to
|
993
|
-
# debug. For example, if you just want to dump rails stuff then do:
|
994
|
-
#
|
995
|
-
# debug "/", what = [:rails]
|
996
|
-
#
|
997
|
-
# And it will only produce the log/mongrel_debug/rails.log file.
|
998
|
-
# Available options are: :objects, :rails, :files, :threads, :params
|
999
|
-
#
|
1000
|
-
# NOTE: Use [:files] to get accesses dumped to stderr like with WEBrick.
|
1001
|
-
def debug(location, what = [:objects, :rails, :files, :threads, :params])
|
1002
|
-
require 'mongrel/debug'
|
1003
|
-
handlers = {
|
1004
|
-
:files => "/handlers/requestlog::access",
|
1005
|
-
:rails => "/handlers/requestlog::files",
|
1006
|
-
:objects => "/handlers/requestlog::objects",
|
1007
|
-
:threads => "/handlers/requestlog::threads",
|
1008
|
-
:params => "/handlers/requestlog::params"
|
1009
|
-
}
|
1010
|
-
|
1011
|
-
# turn on the debugging infrastructure, and ObjectTracker is a pig
|
1012
|
-
ObjectTracker.configure if what.include? :objects
|
1013
|
-
MongrelDbg.configure
|
1014
|
-
|
1015
|
-
# now we roll through each requested debug type, turn it on and load that plugin
|
1016
|
-
what.each do |type|
|
1017
|
-
MongrelDbg.begin_trace type
|
1018
|
-
uri location, :handler => plugin(handlers[type])
|
1019
|
-
end
|
1020
|
-
end
|
1021
|
-
|
1022
|
-
# Used to allow you to let users specify their own configurations
|
1023
|
-
# inside your Configurator setup. You pass it a script name and
|
1024
|
-
# it reads it in and does an eval on the contents passing in the right
|
1025
|
-
# binding so they can put their own Configurator statements.
|
1026
|
-
def run_config(script)
|
1027
|
-
open(script) {|f| eval(f.read, proc {self}) }
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
# Sets up the standard signal handlers that are used on most Ruby
|
1031
|
-
# It only configures if the platform is not win32 and doesn't do
|
1032
|
-
# a HUP signal since this is typically framework specific.
|
1033
|
-
#
|
1034
|
-
# Requires a :pid_file option given to Configurator.new to indicate a file to delete.
|
1035
|
-
# It sets the MongrelConfig.needs_restart attribute if
|
1036
|
-
# the start command should reload. It's up to you to detect this
|
1037
|
-
# and do whatever is needed for a "restart".
|
1038
|
-
#
|
1039
|
-
# This command is safely ignored if the platform is win32 (with a warning)
|
1040
|
-
def setup_signals(options={})
|
1041
|
-
ops = resolve_defaults(options)
|
1042
|
-
|
1043
|
-
# forced shutdown, even if previously restarted (actually just like TERM but for CTRL-C)
|
1044
|
-
trap("INT") { log "INT signal received."; stop(need_restart=false) }
|
1045
|
-
|
1046
|
-
if RUBY_PLATFORM !~ /mswin/
|
1047
|
-
# graceful shutdown
|
1048
|
-
trap("TERM") { log "TERM signal received."; stop }
|
1049
|
-
|
1050
|
-
# restart
|
1051
|
-
trap("USR2") { log "USR2 signal received."; stop(need_restart=true) }
|
1052
|
-
|
1053
|
-
log "Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart)."
|
1054
|
-
else
|
1055
|
-
log "Signals ready. INT => stop (no restart)."
|
1056
|
-
end
|
1057
|
-
end
|
1058
|
-
|
1059
|
-
# Logs a simple message to STDERR (or the mongrel log if in daemon mode).
|
1060
|
-
def log(msg)
|
1061
|
-
STDERR.print "** ", msg, "\n"
|
1062
|
-
end
|
1063
|
-
|
1064
|
-
end
|
1065
759
|
end
|