mongrel 0.3.11 → 0.3.12
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.
- data/Rakefile +4 -2
- data/bin/mongrel_rails +65 -101
- data/bin/mongrel_rails_service +1 -1
- data/bin/mongrel_rails_svc +131 -115
- data/doc/rdoc/classes/Class.html +197 -0
- data/doc/rdoc/classes/Class.src/M000001.html +24 -0
- data/doc/rdoc/classes/Class.src/M000002.html +62 -0
- data/doc/rdoc/classes/Class.src/M000003.html +21 -0
- data/doc/rdoc/classes/Class.src/M000004.html +20 -0
- data/doc/rdoc/classes/IO.html +170 -0
- data/doc/rdoc/classes/IO.src/M000005.html +19 -0
- data/doc/rdoc/classes/IO.src/M000006.html +19 -0
- data/doc/rdoc/classes/Kernel.html +159 -0
- data/doc/rdoc/classes/Kernel.src/M000025.html +19 -0
- data/doc/rdoc/classes/Kernel.src/M000026.html +25 -0
- data/doc/rdoc/classes/Mongrel.html +181 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.html +383 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000090.html +24 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000091.html +47 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000092.html +34 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000093.html +27 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000094.html +25 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000095.html +18 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000096.html +18 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000097.html +18 -0
- data/doc/rdoc/classes/Mongrel/CGIWrapper.src/M000098.html +19 -0
- data/doc/rdoc/classes/Mongrel/Camping.html +177 -0
- data/doc/rdoc/classes/Mongrel/Camping.src/M000048.html +22 -0
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.html +165 -0
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000049.html +18 -0
- data/doc/rdoc/classes/Mongrel/Camping/CampingHandler.src/M000050.html +27 -0
- data/doc/rdoc/classes/Mongrel/Command.html +119 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.html +337 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000029.html +24 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000030.html +41 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000031.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000032.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000033.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000034.html +22 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000035.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000036.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000037.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Base.src/M000038.html +18 -0
- data/doc/rdoc/classes/Mongrel/Command/Registry.html +192 -0
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000039.html +20 -0
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000040.html +25 -0
- data/doc/rdoc/classes/Mongrel/Command/Registry.src/M000041.html +46 -0
- data/doc/rdoc/classes/Mongrel/Configurator.html +563 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000099.html +24 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000100.html +23 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000101.html +18 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000102.html +32 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000103.html +19 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000104.html +31 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000105.html +33 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000106.html +18 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000107.html +25 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000108.html +19 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000109.html +22 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000110.html +21 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000111.html +18 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000112.html +27 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000113.html +46 -0
- data/doc/rdoc/classes/Mongrel/Configurator.src/M000114.html +18 -0
- data/doc/rdoc/classes/Mongrel/Const.html +286 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.html +288 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000058.html +20 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000059.html +42 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000060.html +40 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000061.html +51 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000062.html +40 -0
- data/doc/rdoc/classes/Mongrel/DirHandler.src/M000063.html +18 -0
- data/doc/rdoc/classes/Mongrel/Error404Handler.html +171 -0
- data/doc/rdoc/classes/Mongrel/Error404Handler.src/M000115.html +18 -0
- data/doc/rdoc/classes/Mongrel/Error404Handler.src/M000116.html +18 -0
- data/doc/rdoc/classes/Mongrel/HeaderOut.html +185 -0
- data/doc/rdoc/classes/Mongrel/HeaderOut.src/M000072.html +18 -0
- data/doc/rdoc/classes/Mongrel/HeaderOut.src/M000073.html +18 -0
- data/doc/rdoc/classes/Mongrel/HttpHandler.html +152 -0
- data/doc/rdoc/classes/Mongrel/HttpHandler.src/M000074.html +17 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.html +169 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/M000027.html +18 -0
- data/doc/rdoc/classes/Mongrel/HttpHandlerPlugin.src/M000028.html +17 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.html +271 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000051.html +28 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000052.html +29 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000053.html +29 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000054.html +41 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000055.html +27 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000056.html +27 -0
- data/doc/rdoc/classes/Mongrel/HttpParser.src/M000057.html +28 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.html +222 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000117.html +28 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000118.html +20 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000119.html +20 -0
- data/doc/rdoc/classes/Mongrel/HttpRequest.src/M000120.html +32 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.html +371 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000075.html +26 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000076.html +20 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000077.html +25 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000078.html +22 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000079.html +22 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000080.html +23 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000081.html +18 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000082.html +20 -0
- data/doc/rdoc/classes/Mongrel/HttpResponse.src/M000083.html +18 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.html +360 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000064.html +24 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000065.html +65 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000066.html +24 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000067.html +60 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000068.html +28 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000069.html +18 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000070.html +22 -0
- data/doc/rdoc/classes/Mongrel/HttpServer.src/M000071.html +18 -0
- data/doc/rdoc/classes/Mongrel/Rails.html +112 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.html +223 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000042.html +35 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000043.html +25 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsConfigurator.src/M000044.html +32 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.html +250 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000045.html +22 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000046.html +48 -0
- data/doc/rdoc/classes/Mongrel/Rails/RailsHandler.src/M000047.html +23 -0
- data/doc/rdoc/classes/Mongrel/StopServer.html +117 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.html +301 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000084.html +18 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000085.html +18 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000086.html +39 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000087.html +51 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000088.html +36 -0
- data/doc/rdoc/classes/Mongrel/URIClassifier.src/M000089.html +83 -0
- data/doc/rdoc/classes/MongrelDbg.html +209 -0
- data/doc/rdoc/classes/MongrelDbg.src/M000020.html +19 -0
- data/doc/rdoc/classes/MongrelDbg.src/M000021.html +20 -0
- data/doc/rdoc/classes/MongrelDbg.src/M000022.html +22 -0
- data/doc/rdoc/classes/MongrelDbg.src/M000023.html +21 -0
- data/doc/rdoc/classes/MongrelDbg.src/M000024.html +18 -0
- data/doc/rdoc/classes/ObjectTracker.html +176 -0
- data/doc/rdoc/classes/ObjectTracker.src/M000016.html +22 -0
- data/doc/rdoc/classes/ObjectTracker.src/M000017.html +18 -0
- data/doc/rdoc/classes/ObjectTracker.src/M000018.html +18 -0
- data/doc/rdoc/classes/ObjectTracker.src/M000019.html +46 -0
- data/doc/rdoc/classes/RequestLog.html +113 -0
- data/doc/rdoc/classes/RequestLog/Files.html +144 -0
- data/doc/rdoc/classes/RequestLog/Files.src/M000121.html +19 -0
- data/doc/rdoc/classes/RequestLog/Objects.html +144 -0
- data/doc/rdoc/classes/RequestLog/Objects.src/M000122.html +19 -0
- data/doc/rdoc/classes/RequestLog/Params.html +144 -0
- data/doc/rdoc/classes/RequestLog/Params.src/M000123.html +19 -0
- data/doc/rdoc/classes/Stats.html +306 -0
- data/doc/rdoc/classes/Stats.src/M000009.html +19 -0
- data/doc/rdoc/classes/Stats.src/M000010.html +23 -0
- data/doc/rdoc/classes/Stats.src/M000011.html +26 -0
- data/doc/rdoc/classes/Stats.src/M000012.html +18 -0
- data/doc/rdoc/classes/Stats.src/M000013.html +18 -0
- data/doc/rdoc/classes/Stats.src/M000014.html +19 -0
- data/doc/rdoc/classes/Stats.src/M000015.html +20 -0
- data/doc/rdoc/classes/TCPServer.html +173 -0
- data/doc/rdoc/classes/TCPServer.src/M000007.html +19 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/COPYING.html +756 -0
- data/doc/rdoc/files/LICENSE.html +756 -0
- data/doc/rdoc/files/README.html +302 -0
- data/doc/rdoc/files/ext/http11/http11_c.html +101 -0
- data/doc/rdoc/files/lib/mongrel/camping_rb.html +108 -0
- data/doc/rdoc/files/lib/mongrel/cgi_rb.html +108 -0
- data/doc/rdoc/files/lib/mongrel/command_rb.html +111 -0
- data/doc/rdoc/files/lib/mongrel/debug_rb.html +110 -0
- data/doc/rdoc/files/lib/mongrel/handlers_rb.html +109 -0
- data/doc/rdoc/files/lib/mongrel/init_rb.html +109 -0
- data/doc/rdoc/files/lib/mongrel/rails_rb.html +111 -0
- data/doc/rdoc/files/lib/mongrel/stats_rb.html +117 -0
- data/doc/rdoc/files/lib/mongrel/tcphack_rb.html +109 -0
- data/doc/rdoc/files/lib/mongrel_rb.html +118 -0
- data/doc/rdoc/fr_class_index.html +60 -0
- data/doc/rdoc/fr_file_index.html +40 -0
- data/doc/rdoc/fr_method_index.html +149 -0
- data/doc/rdoc/index.html +24 -0
- data/doc/rdoc/rdoc-style.css +208 -0
- data/examples/builder.rb +29 -0
- data/examples/camping/blog.rb +11 -2
- data/examples/simpletest.rb +20 -7
- data/ext/http11/http11.c +71 -14
- data/ext/http11/http11_parser.c +27 -24
- data/ext/http11/http11_parser.h +2 -1
- data/lib/http11.bundle +0 -0
- data/lib/mongrel.rb +498 -135
- data/lib/mongrel/command.rb +1 -1
- data/lib/mongrel/debug.rb +259 -0
- data/lib/mongrel/handlers.rb +76 -22
- data/lib/mongrel/rails.rb +165 -72
- data/lib/mongrel/stats.rb +71 -0
- data/test/test_configurator.rb +67 -0
- data/test/test_debug.rb +31 -0
- data/test/test_http11.rb +16 -0
- data/test/test_response.rb +5 -0
- data/test/test_stats.rb +28 -0
- metadata +224 -2
data/examples/builder.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'mongrel'
|
|
2
|
+
|
|
3
|
+
class TestPlugin < GemPlugin::Plugin "/handlers"
|
|
4
|
+
include Mongrel::HttpHandlerPlugin
|
|
5
|
+
|
|
6
|
+
def process(request, response)
|
|
7
|
+
STDERR.puts "My options are: #{options.inspect}"
|
|
8
|
+
STDERR.puts "Request Was:"
|
|
9
|
+
STDERR.puts request.params.to_yaml
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
config = Mongrel::Configurator.new :host => "127.0.0.1" do
|
|
14
|
+
load_plugins :includes => ["mongrel"], :excludes => ["rails"]
|
|
15
|
+
daemonize :cwd => Dir.pwd, :log_file => "mongrel.log", :pid_file => "mongrel.pid"
|
|
16
|
+
|
|
17
|
+
listener :port => 3000 do
|
|
18
|
+
uri "/app", :handler => plugin("/handlers/testplugin", :test => "that")
|
|
19
|
+
uri "/app", :handler => Mongrel::DirHandler.new(".")
|
|
20
|
+
load_plugins :includes => ["mongrel", "rails"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
trap("INT") { stop }
|
|
24
|
+
run
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
config.join
|
|
28
|
+
|
|
29
|
+
|
data/examples/camping/blog.rb
CHANGED
|
@@ -277,9 +277,18 @@ if __FILE__ == $0
|
|
|
277
277
|
Blog::Models::Base.logger = Logger.new('camping.log')
|
|
278
278
|
Blog::Models::Base.threaded_connections=false
|
|
279
279
|
Blog.create
|
|
280
|
+
|
|
281
|
+
# Use the Configurator as an example rather than Mongrel::Camping.start
|
|
282
|
+
config = Mongrel::Configurator.new :host => "0.0.0.0" do
|
|
283
|
+
listener :port => 3002 do
|
|
284
|
+
uri "/blog", :handler => CampingHandler.new(Blog)
|
|
285
|
+
uri "/favicon", :handler => Mongrel::Error404Handler.new("")
|
|
286
|
+
trap("INT") { stop }
|
|
287
|
+
run
|
|
288
|
+
end
|
|
289
|
+
end
|
|
280
290
|
|
|
281
|
-
server = Mongrel::Camping::start("0.0.0.0",3002,"/blog",Blog)
|
|
282
291
|
puts "** Blog example is running at http://localhost:3002/blog"
|
|
283
292
|
puts "** Default username is `admin', password is `camping'"
|
|
284
|
-
|
|
293
|
+
config.join
|
|
285
294
|
end
|
data/examples/simpletest.rb
CHANGED
|
@@ -19,18 +19,31 @@ class SimpleHandler < Mongrel::HttpHandler
|
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
class DumbHandler < Mongrel::HttpHandler
|
|
23
|
+
def process(request, response)
|
|
24
|
+
response.start do |head,out|
|
|
25
|
+
head["Content-Type"] = "text/html"
|
|
26
|
+
out.write("test")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
22
32
|
if ARGV.length != 3
|
|
23
33
|
STDERR.puts "usage: simpletest.rb <host> <port> <docroot>"
|
|
24
34
|
exit(1)
|
|
25
35
|
end
|
|
26
36
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
config = Mongrel::Configurator.new :host => ARGV[0], :port => ARGV[1] do
|
|
38
|
+
listener do
|
|
39
|
+
uri "/", :handler => SimpleHandler.new
|
|
40
|
+
uri "/dumb", :handler => DumbHandler.new
|
|
41
|
+
uri "/files", :handler => Mongrel::DirHandler.new(ARGV[2])
|
|
42
|
+
end
|
|
31
43
|
|
|
32
|
-
trap("INT") {
|
|
44
|
+
trap("INT") { stop }
|
|
45
|
+
run
|
|
46
|
+
end
|
|
33
47
|
|
|
34
48
|
puts "Mongrel running on #{ARGV[0]}:#{ARGV[1]} with docroot #{ARGV[2]}"
|
|
35
|
-
|
|
36
|
-
h.acceptor.join
|
|
49
|
+
config.join
|
data/ext/http11/http11.c
CHANGED
|
@@ -16,7 +16,24 @@ static VALUE global_request_method;
|
|
|
16
16
|
static VALUE global_request_uri;
|
|
17
17
|
static VALUE global_query_string;
|
|
18
18
|
static VALUE global_http_version;
|
|
19
|
-
|
|
19
|
+
static VALUE global_content_length;
|
|
20
|
+
static VALUE global_http_content_length;
|
|
21
|
+
static VALUE global_content_type;
|
|
22
|
+
static VALUE global_http_content_type;
|
|
23
|
+
static VALUE global_gateway_interface;
|
|
24
|
+
static VALUE global_gateway_interface_value;
|
|
25
|
+
static VALUE global_interface_value;
|
|
26
|
+
static VALUE global_remote_address;
|
|
27
|
+
static VALUE global_server_name;
|
|
28
|
+
static VALUE global_server_port;
|
|
29
|
+
static VALUE global_server_protocol;
|
|
30
|
+
static VALUE global_server_protocol_value;
|
|
31
|
+
static VALUE global_http_host;
|
|
32
|
+
static VALUE global_mongrel_version;
|
|
33
|
+
static VALUE global_server_software;
|
|
34
|
+
static VALUE global_port_80;
|
|
35
|
+
|
|
36
|
+
#define DEF_GLOBAL(name, val) global_##name = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##name);
|
|
20
37
|
|
|
21
38
|
void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
|
22
39
|
{
|
|
@@ -67,8 +84,37 @@ void http_version(void *data, const char *at, size_t length)
|
|
|
67
84
|
rb_hash_aset(req, global_http_version, val);
|
|
68
85
|
}
|
|
69
86
|
|
|
87
|
+
/** Finalizes the request header to have a bunch of stuff that's
|
|
88
|
+
needed. */
|
|
70
89
|
|
|
71
|
-
|
|
90
|
+
void header_done(void *data, const char *at, size_t length)
|
|
91
|
+
{
|
|
92
|
+
VALUE req = (VALUE)data;
|
|
93
|
+
VALUE temp = Qnil;
|
|
94
|
+
VALUE host = Qnil;
|
|
95
|
+
VALUE port = Qnil;
|
|
96
|
+
char *colon = NULL;
|
|
97
|
+
|
|
98
|
+
rb_hash_aset(req, global_content_length, rb_hash_aref(req, global_http_content_length));
|
|
99
|
+
rb_hash_aset(req, global_content_type, rb_hash_aref(req, global_http_content_type));
|
|
100
|
+
rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
|
|
101
|
+
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
|
102
|
+
// ruby better close strings off with a '\0' dammit
|
|
103
|
+
colon = strchr(RSTRING(temp)->ptr, ':');
|
|
104
|
+
if(colon != NULL) {
|
|
105
|
+
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING(temp)->ptr));
|
|
106
|
+
rb_hash_aset(req, global_server_port,
|
|
107
|
+
rb_str_substr(temp, colon - RSTRING(temp)->ptr+1,
|
|
108
|
+
RSTRING(temp)->len));
|
|
109
|
+
} else {
|
|
110
|
+
rb_hash_aset(req, global_server_name, temp);
|
|
111
|
+
rb_hash_aset(req, global_server_port, global_port_80);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
|
|
116
|
+
rb_hash_aset(req, global_server_software, global_mongrel_version);
|
|
117
|
+
}
|
|
72
118
|
|
|
73
119
|
|
|
74
120
|
void HttpParser_free(void *data) {
|
|
@@ -90,7 +136,8 @@ VALUE HttpParser_alloc(VALUE klass)
|
|
|
90
136
|
hp->request_uri = request_uri;
|
|
91
137
|
hp->query_string = query_string;
|
|
92
138
|
hp->http_version = http_version;
|
|
93
|
-
|
|
139
|
+
hp->header_done = header_done;
|
|
140
|
+
|
|
94
141
|
obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
|
|
95
142
|
|
|
96
143
|
return obj;
|
|
@@ -413,17 +460,27 @@ void Init_http11()
|
|
|
413
460
|
mMongrel = rb_define_module("Mongrel");
|
|
414
461
|
id_handler_map = rb_intern("@handler_map");
|
|
415
462
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
463
|
+
DEF_GLOBAL(http_prefix, "HTTP_");
|
|
464
|
+
DEF_GLOBAL(request_method, "REQUEST_METHOD");
|
|
465
|
+
DEF_GLOBAL(request_uri, "REQUEST_URI");
|
|
466
|
+
DEF_GLOBAL(query_string, "QUERY_STRING");
|
|
467
|
+
DEF_GLOBAL(http_version, "HTTP_VERSION");
|
|
468
|
+
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
|
|
469
|
+
DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
|
|
470
|
+
DEF_GLOBAL(content_type, "CONTENT_TYPE");
|
|
471
|
+
DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
|
|
472
|
+
DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
|
|
473
|
+
DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
|
|
474
|
+
DEF_GLOBAL(remote_address, "REMOTE_ADDR");
|
|
475
|
+
DEF_GLOBAL(server_name, "SERVER_NAME");
|
|
476
|
+
DEF_GLOBAL(server_port, "SERVER_PORT");
|
|
477
|
+
DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
|
|
478
|
+
DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
|
|
479
|
+
DEF_GLOBAL(http_host, "HTTP_HOST");
|
|
480
|
+
DEF_GLOBAL(mongrel_version, "Mongrel 0.3.12");
|
|
481
|
+
DEF_GLOBAL(server_software, "SERVER_SOFTWARE");
|
|
482
|
+
DEF_GLOBAL(port_80, "80");
|
|
483
|
+
|
|
427
484
|
cHttpParser = rb_define_class_under(mMongrel, "HttpParser", rb_cObject);
|
|
428
485
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
|
429
486
|
rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
|
data/ext/http11/http11_parser.c
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#define MARK(S,F) assert((F) - (S)->mark >= 0); (S)->mark = (F);
|
|
10
10
|
|
|
11
11
|
/** machine **/
|
|
12
|
-
#line
|
|
12
|
+
#line 101 "ext/http11/http11_parser.rl"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
/** Data **/
|
|
@@ -21,7 +21,7 @@ static int http_parser_first_final = 53;
|
|
|
21
21
|
|
|
22
22
|
static int http_parser_error = 1;
|
|
23
23
|
|
|
24
|
-
#line
|
|
24
|
+
#line 105 "ext/http11/http11_parser.rl"
|
|
25
25
|
|
|
26
26
|
int http_parser_init(http_parser *parser) {
|
|
27
27
|
int cs = 0;
|
|
@@ -30,7 +30,7 @@ int http_parser_init(http_parser *parser) {
|
|
|
30
30
|
{
|
|
31
31
|
cs = http_parser_start;
|
|
32
32
|
}
|
|
33
|
-
#line
|
|
33
|
+
#line 109 "ext/http11/http11_parser.rl"
|
|
34
34
|
parser->cs = cs;
|
|
35
35
|
parser->body_start = NULL;
|
|
36
36
|
parser->content_len = 0;
|
|
@@ -305,14 +305,17 @@ case 21:
|
|
|
305
305
|
tr40:
|
|
306
306
|
#line 46 "ext/http11/http11_parser.rl"
|
|
307
307
|
{
|
|
308
|
-
parser->body_start = p+1;
|
|
308
|
+
parser->body_start = p+1;
|
|
309
|
+
if(parser->header_done != NULL)
|
|
310
|
+
parser->header_done(parser->data, p, 0);
|
|
311
|
+
goto _out53;
|
|
309
312
|
}
|
|
310
313
|
goto st53;
|
|
311
314
|
st53:
|
|
312
315
|
if ( ++p == pe )
|
|
313
316
|
goto _out53;
|
|
314
317
|
case 53:
|
|
315
|
-
#line
|
|
318
|
+
#line 319 "ext/http11/http11_parser.c"
|
|
316
319
|
goto st1;
|
|
317
320
|
tr36:
|
|
318
321
|
#line 16 "ext/http11/http11_parser.rl"
|
|
@@ -322,7 +325,7 @@ st22:
|
|
|
322
325
|
if ( ++p == pe )
|
|
323
326
|
goto _out22;
|
|
324
327
|
case 22:
|
|
325
|
-
#line
|
|
328
|
+
#line 329 "ext/http11/http11_parser.c"
|
|
326
329
|
switch( (*p) ) {
|
|
327
330
|
case 33: goto st22;
|
|
328
331
|
case 58: goto tr32;
|
|
@@ -357,7 +360,7 @@ st23:
|
|
|
357
360
|
if ( ++p == pe )
|
|
358
361
|
goto _out23;
|
|
359
362
|
case 23:
|
|
360
|
-
#line
|
|
363
|
+
#line 364 "ext/http11/http11_parser.c"
|
|
361
364
|
if ( (*p) == 13 )
|
|
362
365
|
goto tr49;
|
|
363
366
|
goto tr52;
|
|
@@ -369,7 +372,7 @@ st24:
|
|
|
369
372
|
if ( ++p == pe )
|
|
370
373
|
goto _out24;
|
|
371
374
|
case 24:
|
|
372
|
-
#line
|
|
375
|
+
#line 376 "ext/http11/http11_parser.c"
|
|
373
376
|
if ( (*p) == 13 )
|
|
374
377
|
goto tr49;
|
|
375
378
|
goto st24;
|
|
@@ -381,7 +384,7 @@ st25:
|
|
|
381
384
|
if ( ++p == pe )
|
|
382
385
|
goto _out25;
|
|
383
386
|
case 25:
|
|
384
|
-
#line
|
|
387
|
+
#line 388 "ext/http11/http11_parser.c"
|
|
385
388
|
switch( (*p) ) {
|
|
386
389
|
case 43: goto st25;
|
|
387
390
|
case 58: goto st26;
|
|
@@ -406,7 +409,7 @@ st26:
|
|
|
406
409
|
if ( ++p == pe )
|
|
407
410
|
goto _out26;
|
|
408
411
|
case 26:
|
|
409
|
-
#line
|
|
412
|
+
#line 413 "ext/http11/http11_parser.c"
|
|
410
413
|
switch( (*p) ) {
|
|
411
414
|
case 32: goto tr34;
|
|
412
415
|
case 37: goto st27;
|
|
@@ -454,7 +457,7 @@ st29:
|
|
|
454
457
|
if ( ++p == pe )
|
|
455
458
|
goto _out29;
|
|
456
459
|
case 29:
|
|
457
|
-
#line
|
|
460
|
+
#line 461 "ext/http11/http11_parser.c"
|
|
458
461
|
switch( (*p) ) {
|
|
459
462
|
case 32: goto tr34;
|
|
460
463
|
case 37: goto st31;
|
|
@@ -525,7 +528,7 @@ st33:
|
|
|
525
528
|
if ( ++p == pe )
|
|
526
529
|
goto _out33;
|
|
527
530
|
case 33:
|
|
528
|
-
#line
|
|
531
|
+
#line 532 "ext/http11/http11_parser.c"
|
|
529
532
|
switch( (*p) ) {
|
|
530
533
|
case 32: goto tr46;
|
|
531
534
|
case 37: goto tr51;
|
|
@@ -547,7 +550,7 @@ st34:
|
|
|
547
550
|
if ( ++p == pe )
|
|
548
551
|
goto _out34;
|
|
549
552
|
case 34:
|
|
550
|
-
#line
|
|
553
|
+
#line 554 "ext/http11/http11_parser.c"
|
|
551
554
|
switch( (*p) ) {
|
|
552
555
|
case 32: goto tr46;
|
|
553
556
|
case 37: goto st35;
|
|
@@ -569,7 +572,7 @@ st35:
|
|
|
569
572
|
if ( ++p == pe )
|
|
570
573
|
goto _out35;
|
|
571
574
|
case 35:
|
|
572
|
-
#line
|
|
575
|
+
#line 576 "ext/http11/http11_parser.c"
|
|
573
576
|
if ( (*p) < 65 ) {
|
|
574
577
|
if ( 48 <= (*p) && (*p) <= 57 )
|
|
575
578
|
goto st36;
|
|
@@ -600,7 +603,7 @@ st37:
|
|
|
600
603
|
if ( ++p == pe )
|
|
601
604
|
goto _out37;
|
|
602
605
|
case 37:
|
|
603
|
-
#line
|
|
606
|
+
#line 607 "ext/http11/http11_parser.c"
|
|
604
607
|
if ( (*p) == 69 )
|
|
605
608
|
goto st38;
|
|
606
609
|
goto st1;
|
|
@@ -619,7 +622,7 @@ st39:
|
|
|
619
622
|
if ( ++p == pe )
|
|
620
623
|
goto _out39;
|
|
621
624
|
case 39:
|
|
622
|
-
#line
|
|
625
|
+
#line 626 "ext/http11/http11_parser.c"
|
|
623
626
|
if ( (*p) == 69 )
|
|
624
627
|
goto st40;
|
|
625
628
|
goto st1;
|
|
@@ -645,7 +648,7 @@ st42:
|
|
|
645
648
|
if ( ++p == pe )
|
|
646
649
|
goto _out42;
|
|
647
650
|
case 42:
|
|
648
|
-
#line
|
|
651
|
+
#line 652 "ext/http11/http11_parser.c"
|
|
649
652
|
if ( (*p) == 80 )
|
|
650
653
|
goto st43;
|
|
651
654
|
goto st1;
|
|
@@ -692,7 +695,7 @@ st48:
|
|
|
692
695
|
if ( ++p == pe )
|
|
693
696
|
goto _out48;
|
|
694
697
|
case 48:
|
|
695
|
-
#line
|
|
698
|
+
#line 699 "ext/http11/http11_parser.c"
|
|
696
699
|
switch( (*p) ) {
|
|
697
700
|
case 79: goto st49;
|
|
698
701
|
case 85: goto st38;
|
|
@@ -713,7 +716,7 @@ st50:
|
|
|
713
716
|
if ( ++p == pe )
|
|
714
717
|
goto _out50;
|
|
715
718
|
case 50:
|
|
716
|
-
#line
|
|
719
|
+
#line 720 "ext/http11/http11_parser.c"
|
|
717
720
|
if ( (*p) == 82 )
|
|
718
721
|
goto st51;
|
|
719
722
|
goto st1;
|
|
@@ -788,15 +791,15 @@ case 52:
|
|
|
788
791
|
|
|
789
792
|
_out: {}
|
|
790
793
|
}
|
|
791
|
-
#line
|
|
794
|
+
#line 128 "ext/http11/http11_parser.rl"
|
|
792
795
|
|
|
793
796
|
parser->cs = cs;
|
|
794
797
|
parser->nread = p - buffer;
|
|
795
798
|
if(parser->body_start) {
|
|
796
799
|
/* final \r\n combo encountered so stop right here */
|
|
797
800
|
|
|
798
|
-
#line
|
|
799
|
-
#line
|
|
801
|
+
#line 802 "ext/http11/http11_parser.c"
|
|
802
|
+
#line 134 "ext/http11/http11_parser.rl"
|
|
800
803
|
parser->nread++;
|
|
801
804
|
}
|
|
802
805
|
|
|
@@ -808,8 +811,8 @@ int http_parser_finish(http_parser *parser)
|
|
|
808
811
|
int cs = parser->cs;
|
|
809
812
|
|
|
810
813
|
|
|
811
|
-
#line
|
|
812
|
-
#line
|
|
814
|
+
#line 815 "ext/http11/http11_parser.c"
|
|
815
|
+
#line 145 "ext/http11/http11_parser.rl"
|
|
813
816
|
|
|
814
817
|
parser->cs = cs;
|
|
815
818
|
|
data/ext/http11/http11_parser.h
CHANGED
data/lib/http11.bundle
ADDED
|
Binary file
|
data/lib/mongrel.rb
CHANGED
|
@@ -5,8 +5,8 @@ require 'stringio'
|
|
|
5
5
|
require 'mongrel/cgi'
|
|
6
6
|
require 'mongrel/handlers'
|
|
7
7
|
require 'mongrel/command'
|
|
8
|
-
require 'timeout'
|
|
9
8
|
require 'mongrel/tcphack'
|
|
9
|
+
require 'yaml'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
# Mongrel module containing all of the classes (include C extensions) for running
|
|
@@ -88,73 +88,52 @@ module Mongrel
|
|
|
88
88
|
# REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
|
|
89
89
|
# too taxing on performance.
|
|
90
90
|
module Const
|
|
91
|
-
|
|
92
|
-
PATH_INFO="PATH_INFO"
|
|
93
|
-
# This is the intial part that your handler is identified as by URIClassifier.
|
|
94
|
-
SCRIPT_NAME="SCRIPT_NAME"
|
|
95
|
-
# The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
|
|
96
|
-
REQUEST_URI='REQUEST_URI'
|
|
97
|
-
|
|
98
|
-
# Content length (also available as HTTP_CONTENT_LENGTH).
|
|
99
|
-
CONTENT_LENGTH='CONTENT_LENGTH'
|
|
100
|
-
|
|
101
|
-
# Content length (also available as CONTENT_LENGTH).
|
|
102
|
-
HTTP_CONTENT_LENGTH='HTTP_CONTENT_LENGTH'
|
|
103
|
-
|
|
104
|
-
# Content type (also available as HTTP_CONTENT_TYPE).
|
|
105
|
-
CONTENT_TYPE='CONTENT_TYPE'
|
|
106
|
-
|
|
107
|
-
# Content type (also available as CONTENT_TYPE).
|
|
108
|
-
HTTP_CONTENT_TYPE='HTTP_CONTENT_TYPE'
|
|
109
|
-
|
|
110
|
-
# Gateway interface key in the HttpRequest parameters.
|
|
111
|
-
GATEWAY_INTERFACE='GATEWAY_INTERFACE'
|
|
112
|
-
# We claim to support CGI/1.2.
|
|
113
|
-
GATEWAY_INTERFACE_VALUE='CGI/1.2'
|
|
114
|
-
|
|
115
|
-
# Hosts remote IP address. Mongrel does not do DNS resolves since that slows
|
|
116
|
-
# processing down considerably.
|
|
117
|
-
REMOTE_ADDR='REMOTE_ADDR'
|
|
118
|
-
|
|
119
|
-
# This is not given since Mongrel does not do DNS resolves. It is only here for
|
|
120
|
-
# completeness for the CGI standard.
|
|
121
|
-
REMOTE_HOST='REMOTE_HOST'
|
|
91
|
+
DATE = "Date".freeze
|
|
122
92
|
|
|
123
|
-
#
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# The port of our server as given by the HttpServer.new(host,port) call.
|
|
127
|
-
SERVER_PORT='SERVER_PORT'
|
|
93
|
+
# This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this.
|
|
94
|
+
PATH_INFO="PATH_INFO".freeze
|
|
128
95
|
|
|
129
|
-
#
|
|
130
|
-
|
|
96
|
+
# This is the intial part that your handler is identified as by URIClassifier.
|
|
97
|
+
SCRIPT_NAME="SCRIPT_NAME".freeze
|
|
131
98
|
|
|
132
|
-
#
|
|
133
|
-
|
|
134
|
-
# Mongrel claims to support HTTP/1.1.
|
|
135
|
-
SERVER_PROTOCOL_VALUE='HTTP/1.1'
|
|
99
|
+
# The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
|
|
100
|
+
REQUEST_URI='REQUEST_URI'.freeze
|
|
136
101
|
|
|
137
|
-
|
|
138
|
-
SERVER_SOFTWARE='SERVER_SOFTWARE'
|
|
139
|
-
|
|
140
|
-
# Current Mongrel version (used for SERVER_SOFTWARE and other response headers).
|
|
141
|
-
MONGREL_VERSION='Mongrel 0.3.10'
|
|
102
|
+
MONGREL_VERSION="0.3.12".freeze
|
|
142
103
|
|
|
143
104
|
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
|
144
|
-
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
|
|
105
|
+
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
|
|
106
|
+
|
|
107
|
+
CONTENT_LENGTH="CONTENT_LENGTH".freeze
|
|
145
108
|
|
|
146
109
|
# A common header for indicating the server is too busy. Not used yet.
|
|
147
|
-
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
|
|
110
|
+
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
|
148
111
|
|
|
149
112
|
# The basic max request size we'll try to read.
|
|
150
113
|
CHUNK_SIZE=(16 * 1024)
|
|
151
114
|
|
|
115
|
+
# Format to generate a correct RFC 1123 date. rdoc for Time is wrong, there is no httpdate function.
|
|
116
|
+
RFC_1123_DATE_FORMAT="%a, %d %B %Y %H:%M:%S GMT".freeze
|
|
117
|
+
|
|
118
|
+
# A frozen format for this is about 15% faster
|
|
119
|
+
STATUS_FORMAT = "HTTP/1.1 %d %s\r\nContent-Length: %d\r\nConnection: close\r\n".freeze
|
|
120
|
+
CONTENT_TYPE = "Content-Type".freeze
|
|
121
|
+
LAST_MODIFIED = "Last-Modified".freeze
|
|
122
|
+
ETAG = "ETag".freeze
|
|
123
|
+
SLASH = "/".freeze
|
|
124
|
+
REQUEST_METHOD="REQUEST_METHOD".freeze
|
|
125
|
+
GET="GET".freeze
|
|
126
|
+
HEAD="HEAD".freeze
|
|
127
|
+
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
|
128
|
+
ETAG_FORMAT="\"%x-%x-%x\"".freeze
|
|
129
|
+
HEADER_FORMAT="%s: %s\r\n".freeze
|
|
130
|
+
LINE_END="\r\n".freeze
|
|
152
131
|
end
|
|
153
132
|
|
|
154
133
|
|
|
155
134
|
# When a handler is found for a registered URI then this class is constructed
|
|
156
135
|
# and passed to your HttpHandler::process method. You should assume that
|
|
157
|
-
# *one* handler processes all requests. Included in the
|
|
136
|
+
# *one* handler processes all requests. Included in the HttpRequest is a
|
|
158
137
|
# HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
|
|
159
138
|
# which is a string containing the request body (raw for now).
|
|
160
139
|
#
|
|
@@ -172,28 +151,49 @@ module Mongrel
|
|
|
172
151
|
@body = initial_body || ""
|
|
173
152
|
@params = params
|
|
174
153
|
@socket = socket
|
|
175
|
-
|
|
176
|
-
# fix up the CGI requirements
|
|
177
|
-
params[Const::CONTENT_LENGTH] = params[Const::HTTP_CONTENT_LENGTH] || 0
|
|
178
|
-
params[Const::CONTENT_TYPE] = params[Const::HTTP_CONTENT_TYPE] if params[Const::HTTP_CONTENT_TYPE]
|
|
179
|
-
params[Const::GATEWAY_INTERFACE]=Const::GATEWAY_INTERFACE_VALUE
|
|
180
|
-
params[Const::REMOTE_ADDR]=socket.peeraddr[3]
|
|
181
|
-
if params.has_key? Const::HTTP_HOST
|
|
182
|
-
host,port = params[Const::HTTP_HOST].split(":")
|
|
183
|
-
params[Const::SERVER_NAME]=host
|
|
184
|
-
params[Const::SERVER_PORT]=port || 80
|
|
185
|
-
end
|
|
186
|
-
params[Const::SERVER_PROTOCOL]=Const::SERVER_PROTOCOL_VALUE
|
|
187
|
-
params[Const::SERVER_SOFTWARE]=Const::MONGREL_VERSION
|
|
188
|
-
|
|
189
154
|
|
|
190
155
|
# now, if the initial_body isn't long enough for the content length we have to fill it
|
|
191
156
|
# TODO: adapt for big ass stuff by writing to a temp file
|
|
192
|
-
clen = params[Const::
|
|
157
|
+
clen = params[Const::CONTENT_LENGTH].to_i
|
|
193
158
|
if @body.length < clen
|
|
194
159
|
@body << @socket.read(clen - @body.length)
|
|
195
160
|
end
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def self.escape(s)
|
|
165
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
|
166
|
+
'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
|
167
|
+
}.tr(' ', '+')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def self.unescape(s)
|
|
172
|
+
s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
|
|
173
|
+
[$1.delete('%')].pack('H*')
|
|
174
|
+
}
|
|
196
175
|
end
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def self.query_parse(qs, d = '&;')
|
|
179
|
+
params = {}
|
|
180
|
+
(qs||'').split(/[#{d}] */n).inject(params) { |h,p|
|
|
181
|
+
k, v=unescape(p).split('=',2)
|
|
182
|
+
if cur = params[k]
|
|
183
|
+
if cur.class == Array
|
|
184
|
+
params[k] << v
|
|
185
|
+
else
|
|
186
|
+
params[k] = [cur, v]
|
|
187
|
+
end
|
|
188
|
+
else
|
|
189
|
+
params[k] = v
|
|
190
|
+
end
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return params
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
|
|
197
197
|
end
|
|
198
198
|
|
|
199
199
|
|
|
@@ -213,11 +213,9 @@ module Mongrel
|
|
|
213
213
|
|
|
214
214
|
# Simply writes "#{key}: #{value}" to an output buffer.
|
|
215
215
|
def[]=(key,value)
|
|
216
|
-
@out.write(key)
|
|
217
|
-
@out.write(": ")
|
|
218
|
-
@out.write(value)
|
|
219
|
-
@out.write("\r\n")
|
|
216
|
+
@out.write(Const::HEADER_FORMAT % [key, value])
|
|
220
217
|
end
|
|
218
|
+
|
|
221
219
|
end
|
|
222
220
|
|
|
223
221
|
# Writes and controls your response to the client using the HTTP/1.1 specification.
|
|
@@ -255,48 +253,79 @@ module Mongrel
|
|
|
255
253
|
attr_reader :header
|
|
256
254
|
attr_reader :status
|
|
257
255
|
attr_writer :status
|
|
256
|
+
attr_reader :body_sent
|
|
257
|
+
attr_reader :header_sent
|
|
258
|
+
attr_reader :status_sent
|
|
258
259
|
|
|
259
|
-
def initialize(socket)
|
|
260
|
+
def initialize(socket, filter = nil)
|
|
260
261
|
@socket = socket
|
|
261
262
|
@body = StringIO.new
|
|
262
263
|
@status = 404
|
|
263
264
|
@header = HeaderOut.new(StringIO.new)
|
|
265
|
+
@header[Const::DATE] = HttpServer.httpdate(Time.now)
|
|
266
|
+
@filter = filter
|
|
267
|
+
@body_sent = false
|
|
268
|
+
@header_sent = false
|
|
269
|
+
@status_sent = false
|
|
264
270
|
end
|
|
265
271
|
|
|
266
272
|
# Receives a block passing it the header and body for you to work with.
|
|
267
273
|
# When the block is finished it writes everything you've done to
|
|
268
274
|
# the socket in the proper order. This lets you intermix header and
|
|
269
|
-
# body content as needed.
|
|
270
|
-
|
|
275
|
+
# body content as needed. Handlers are able to modify pretty much
|
|
276
|
+
# any part of the request in the chain, and can stop further processing
|
|
277
|
+
# by simple passing "finalize=true" to the start method. By default
|
|
278
|
+
# all handlers run and then mongrel finalizes the request when they're
|
|
279
|
+
# all done.
|
|
280
|
+
def start(status=200, finalize=false)
|
|
271
281
|
@status = status.to_i
|
|
272
282
|
yield @header, @body
|
|
273
|
-
finished
|
|
283
|
+
finished if finalize
|
|
274
284
|
end
|
|
275
285
|
|
|
276
286
|
# Primarily used in exception handling to reset the response output in order to write
|
|
277
|
-
# an alternative response.
|
|
287
|
+
# an alternative response. It will abort with an exception if you have already
|
|
288
|
+
# sent the header or the body. This is pretty catastrophic actually.
|
|
278
289
|
def reset
|
|
279
|
-
@
|
|
280
|
-
|
|
290
|
+
if @body_sent
|
|
291
|
+
raise "You have already sent the request body."
|
|
292
|
+
elsif @header_sent
|
|
293
|
+
raise "You have already sent the request headers."
|
|
294
|
+
else
|
|
295
|
+
@header.out.rewind
|
|
296
|
+
@body.rewind
|
|
297
|
+
end
|
|
281
298
|
end
|
|
282
299
|
|
|
283
|
-
def send_status
|
|
284
|
-
|
|
285
|
-
|
|
300
|
+
def send_status(content_length=nil)
|
|
301
|
+
if not @status_sent
|
|
302
|
+
content_length ||= @body.length
|
|
303
|
+
@socket.write(Const::STATUS_FORMAT % [status, HTTP_STATUS_CODES[@status], content_length])
|
|
304
|
+
@status_sent = true
|
|
305
|
+
end
|
|
286
306
|
end
|
|
287
307
|
|
|
288
308
|
def send_header
|
|
289
|
-
@
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
if not @header_sent
|
|
310
|
+
@header.out.rewind
|
|
311
|
+
@socket.write(@header.out.read + Const::LINE_END)
|
|
312
|
+
@header_sent = true
|
|
313
|
+
end
|
|
292
314
|
end
|
|
293
315
|
|
|
294
316
|
def send_body
|
|
295
|
-
@
|
|
296
|
-
|
|
297
|
-
|
|
317
|
+
if not @body_sent
|
|
318
|
+
@body.rewind
|
|
319
|
+
# connection: close is also added to ensure that the client does not pipeline.
|
|
320
|
+
@socket.write(@body.read)
|
|
321
|
+
@body_sent = true
|
|
322
|
+
end
|
|
298
323
|
end
|
|
299
324
|
|
|
325
|
+
def write(data)
|
|
326
|
+
@socket.write(data)
|
|
327
|
+
end
|
|
328
|
+
|
|
300
329
|
# This takes whatever has been done to header and body and then writes it in the
|
|
301
330
|
# proper format to make an HTTP/1.1 response.
|
|
302
331
|
def finished
|
|
@@ -304,6 +333,11 @@ module Mongrel
|
|
|
304
333
|
send_header
|
|
305
334
|
send_body
|
|
306
335
|
end
|
|
336
|
+
|
|
337
|
+
def done
|
|
338
|
+
(@status_sent and @header_sent and @body_sent)
|
|
339
|
+
end
|
|
340
|
+
|
|
307
341
|
end
|
|
308
342
|
|
|
309
343
|
|
|
@@ -327,41 +361,30 @@ module Mongrel
|
|
|
327
361
|
# hold your breath until Ruby 1.9 is actually finally useful.
|
|
328
362
|
class HttpServer
|
|
329
363
|
attr_reader :acceptor
|
|
364
|
+
attr_reader :workers
|
|
365
|
+
attr_reader :classifier
|
|
330
366
|
|
|
331
367
|
# Creates a working server on host:port (strange things happen if port isn't a Number).
|
|
332
|
-
# Use HttpServer::run to start the server.
|
|
333
|
-
#
|
|
334
|
-
# The num_processors variable has varying affects on how requests are processed. You'd
|
|
335
|
-
# think adding more processing threads (processors) would make the server faster, but
|
|
336
|
-
# that's just not true. There's actually an effect of how Ruby does threads such that
|
|
337
|
-
# the more processors waiting on the request queue, the slower the system is to handle
|
|
338
|
-
# each request. But, the lower the number of processors the fewer concurrent responses
|
|
339
|
-
# the server can make.
|
|
368
|
+
# Use HttpServer::run to start the server and HttpServer.acceptor.join to
|
|
369
|
+
# join the thread that's processing incoming requests on the socket.
|
|
340
370
|
#
|
|
341
|
-
#
|
|
342
|
-
#
|
|
343
|
-
#
|
|
344
|
-
#
|
|
345
|
-
|
|
371
|
+
# The num_processors optional argument is the maximum number of concurrent
|
|
372
|
+
# processors to accept, anything over this is closed immediately to maintain
|
|
373
|
+
# server processing performance. This may seem mean but it is the most efficient
|
|
374
|
+
# way to deal with overload. Other schemes involve still parsing the client's request
|
|
375
|
+
# which defeats the point of an overload handling system.
|
|
376
|
+
#
|
|
377
|
+
# The timeout parameter is a sleep timeout (in hundredths of a second) that is placed between
|
|
378
|
+
# socket.accept calls in order to give the server a cheap throttle time. It defaults to 0 and
|
|
379
|
+
# actually if it is 0 then the sleep is not done at all.
|
|
380
|
+
def initialize(host, port, num_processors=(2**30-1), timeout=0)
|
|
346
381
|
@socket = TCPServer.new(host, port)
|
|
347
|
-
|
|
348
382
|
@classifier = URIClassifier.new
|
|
349
|
-
@req_queue = Queue.new
|
|
350
383
|
@host = host
|
|
351
384
|
@port = port
|
|
352
|
-
@
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
num_processors.times do |i|
|
|
356
|
-
@processors << Thread.new do
|
|
357
|
-
while client = @req_queue.deq
|
|
358
|
-
Timeout::timeout(timeout) do
|
|
359
|
-
process_client(client)
|
|
360
|
-
end
|
|
361
|
-
end
|
|
362
|
-
end
|
|
363
|
-
end
|
|
364
|
-
|
|
385
|
+
@workers = ThreadGroup.new
|
|
386
|
+
@timeout = timeout
|
|
387
|
+
@num_processors = num_processors
|
|
365
388
|
end
|
|
366
389
|
|
|
367
390
|
|
|
@@ -374,19 +397,31 @@ module Mongrel
|
|
|
374
397
|
begin
|
|
375
398
|
parser = HttpParser.new
|
|
376
399
|
params = {}
|
|
400
|
+
|
|
377
401
|
data = client.readpartial(Const::CHUNK_SIZE)
|
|
378
402
|
|
|
379
403
|
while true
|
|
380
404
|
nread = parser.execute(params, data)
|
|
405
|
+
|
|
381
406
|
if parser.finished?
|
|
382
|
-
script_name, path_info,
|
|
407
|
+
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_URI])
|
|
383
408
|
|
|
384
|
-
if
|
|
409
|
+
if handlers
|
|
385
410
|
params[Const::PATH_INFO] = path_info
|
|
386
411
|
params[Const::SCRIPT_NAME] = script_name
|
|
412
|
+
|
|
387
413
|
request = HttpRequest.new(params, data[nread ... data.length], client)
|
|
388
414
|
response = HttpResponse.new(client)
|
|
389
|
-
|
|
415
|
+
|
|
416
|
+
handlers.each do |handler|
|
|
417
|
+
handler.process(request, response)
|
|
418
|
+
break if response.done
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
if not response.done
|
|
422
|
+
response.finished
|
|
423
|
+
end
|
|
424
|
+
|
|
390
425
|
else
|
|
391
426
|
client.write(Const::ERROR_404_RESPONSE)
|
|
392
427
|
end
|
|
@@ -399,11 +434,7 @@ module Mongrel
|
|
|
399
434
|
data << client.readpartial(Const::CHUNK_SIZE)
|
|
400
435
|
end
|
|
401
436
|
end
|
|
402
|
-
rescue EOFError
|
|
403
|
-
# ignored
|
|
404
|
-
rescue Errno::ECONNRESET
|
|
405
|
-
# ignored
|
|
406
|
-
rescue Errno::EPIPE
|
|
437
|
+
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL
|
|
407
438
|
# ignored
|
|
408
439
|
rescue => details
|
|
409
440
|
STDERR.puts "ERROR(#{details.class}): #{details}"
|
|
@@ -413,16 +444,45 @@ module Mongrel
|
|
|
413
444
|
end
|
|
414
445
|
end
|
|
415
446
|
|
|
447
|
+
# Used internally to kill off any worker threads that have taken too long
|
|
448
|
+
# to complete processing. Only called if there are too many processors
|
|
449
|
+
# currently servicing.
|
|
450
|
+
def reap_dead_workers(worker_list)
|
|
451
|
+
mark = Time.now
|
|
452
|
+
worker_list.each do |w|
|
|
453
|
+
if mark - w[:started_on] > 10 * @timeout
|
|
454
|
+
STDERR.puts "Thread #{w.inspect} is too old, killing."
|
|
455
|
+
w.raise(StopServer.new("Timed out thread."))
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
|
|
416
461
|
# Runs the thing. It returns the thread used so you can "join" it. You can also
|
|
417
462
|
# access the HttpServer::acceptor attribute to get the thread later.
|
|
418
463
|
def run
|
|
419
464
|
BasicSocket.do_not_reverse_lookup=true
|
|
420
|
-
@acceptor = Thread.new do
|
|
421
|
-
Thread.current[:stopped] = false
|
|
422
465
|
|
|
423
|
-
|
|
466
|
+
@acceptor = Thread.new do
|
|
467
|
+
while true
|
|
424
468
|
begin
|
|
425
|
-
|
|
469
|
+
client = @socket.accept
|
|
470
|
+
worker_list = @workers.list
|
|
471
|
+
if worker_list.length >= @num_processors
|
|
472
|
+
STDERR.puts "Server overloaded with #{worker_list.length} processors (#@num_processors max). Dropping connection."
|
|
473
|
+
client.close
|
|
474
|
+
reap_dead_workers(worker_list)
|
|
475
|
+
else
|
|
476
|
+
thread = Thread.new do
|
|
477
|
+
process_client(client)
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
thread[:started_on] = Time.now
|
|
481
|
+
thread.priority=1
|
|
482
|
+
@workers.add(thread)
|
|
483
|
+
|
|
484
|
+
sleep @timeout/100 if @timeout > 0
|
|
485
|
+
end
|
|
426
486
|
rescue StopServer
|
|
427
487
|
STDERR.puts "Server stopped. Exiting."
|
|
428
488
|
@socket.close if not @socket.closed?
|
|
@@ -435,17 +495,14 @@ module Mongrel
|
|
|
435
495
|
|
|
436
496
|
# now that processing is done we feed enough false onto the request queue to get
|
|
437
497
|
# each processor to exit and stop processing.
|
|
438
|
-
@processors.length.times { @req_queue << false }
|
|
439
498
|
|
|
440
499
|
# finally we wait until the queue is empty
|
|
441
|
-
while @
|
|
442
|
-
STDERR.puts "Shutdown waiting for #{@
|
|
500
|
+
while @workers.list.length > 0
|
|
501
|
+
STDERR.puts "Shutdown waiting for #{@workers.list.length} requests" if @workers.list.length > 0
|
|
443
502
|
sleep 1
|
|
444
503
|
end
|
|
445
504
|
end
|
|
446
505
|
|
|
447
|
-
@acceptor.priority = 1
|
|
448
|
-
|
|
449
506
|
return @acceptor
|
|
450
507
|
end
|
|
451
508
|
|
|
@@ -454,11 +511,22 @@ module Mongrel
|
|
|
454
511
|
# found in the prefix of a request then your handler's HttpHandler::process method
|
|
455
512
|
# is called. See Mongrel::URIClassifier#register for more information.
|
|
456
513
|
def register(uri, handler)
|
|
457
|
-
@classifier.
|
|
514
|
+
script_name, path_info, handlers = @classifier.resolve(uri)
|
|
515
|
+
|
|
516
|
+
if not handlers
|
|
517
|
+
@classifier.register(uri, [handler])
|
|
518
|
+
else
|
|
519
|
+
if path_info.length == 0 or (script_name == Const::SLASH and path_info == Const::SLASH)
|
|
520
|
+
handlers << handler
|
|
521
|
+
else
|
|
522
|
+
@classifier.register(uri, [handler])
|
|
523
|
+
end
|
|
524
|
+
end
|
|
458
525
|
end
|
|
459
526
|
|
|
460
|
-
# Removes any
|
|
461
|
-
# for more information.
|
|
527
|
+
# Removes any handlers registered at the given URI. See Mongrel::URIClassifier#unregister
|
|
528
|
+
# for more information. Remember this removes them *all* so the entire
|
|
529
|
+
# processing chain goes away.
|
|
462
530
|
def unregister(uri)
|
|
463
531
|
@classifier.unregister(uri)
|
|
464
532
|
end
|
|
@@ -467,13 +535,308 @@ module Mongrel
|
|
|
467
535
|
# off the request queue before finally exiting.
|
|
468
536
|
def stop
|
|
469
537
|
stopper = Thread.new do
|
|
470
|
-
@acceptor[:stopped] = true
|
|
471
538
|
exc = StopServer.new
|
|
472
539
|
@acceptor.raise(exc)
|
|
473
540
|
end
|
|
474
541
|
stopper.priority = 10
|
|
475
542
|
end
|
|
476
543
|
|
|
544
|
+
# Given the a time object it converts it to GMT and applies the RFC1123 format to it.
|
|
545
|
+
def HttpServer.httpdate(date)
|
|
546
|
+
date.gmtime.strftime(Const::RFC_1123_DATE_FORMAT)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
# Implements a simple DSL for configuring a Mongrel server for your
|
|
553
|
+
# purposes. More used by framework implementers to setup Mongrel
|
|
554
|
+
# how they like, but could be used by regular folks to add more things
|
|
555
|
+
# to an existing mongrel configuration.
|
|
556
|
+
#
|
|
557
|
+
# It is used like this:
|
|
558
|
+
#
|
|
559
|
+
# require 'mongrel'
|
|
560
|
+
# config = Mongrel::Configurator.new :host => "127.0.0.1" do
|
|
561
|
+
# listener :port => 3000 do
|
|
562
|
+
# uri "/app", :handler => Mongrel::DirHandler.new(".", load_mime_map("mime.yaml"))
|
|
563
|
+
# end
|
|
564
|
+
# run
|
|
565
|
+
# end
|
|
566
|
+
#
|
|
567
|
+
# This will setup a simple DirHandler at the current directory and load additional
|
|
568
|
+
# mime types from mimy.yaml. The :host => "127.0.0.1" is actually not
|
|
569
|
+
# specific to the servers but just a hash of default parameters that all
|
|
570
|
+
# server or uri calls receive.
|
|
571
|
+
#
|
|
572
|
+
# When you are inside the block after Mongrel::Configurator.new you can simply
|
|
573
|
+
# call functions that are part of Configurator (like server, uri, daemonize, etc)
|
|
574
|
+
# without having to refer to anything else. You can also call these functions on
|
|
575
|
+
# the resulting object directly for additional configuration.
|
|
576
|
+
#
|
|
577
|
+
# A major thing about Configurator is that it actually lets you configure
|
|
578
|
+
# multiple listeners for any hosts and ports you want. These are kept in a
|
|
579
|
+
# map config.listeners so you can get to them.
|
|
580
|
+
class Configurator
|
|
581
|
+
attr_reader :listeners
|
|
582
|
+
attr_reader :defaults
|
|
583
|
+
attr_reader :needs_restart
|
|
584
|
+
|
|
585
|
+
# You pass in initial defaults and then a block to continue configuring.
|
|
586
|
+
def initialize(defaults={}, &blk)
|
|
587
|
+
@listeners = {}
|
|
588
|
+
@defaults = defaults
|
|
589
|
+
@needs_restart = false
|
|
590
|
+
|
|
591
|
+
if blk
|
|
592
|
+
cloaker(&blk).bind(self).call
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Do not call this. You were warned.
|
|
597
|
+
def cloaker &blk
|
|
598
|
+
(class << self; self; end).class_eval do
|
|
599
|
+
define_method :cloaker_, &blk
|
|
600
|
+
meth = instance_method( :cloaker_ )
|
|
601
|
+
remove_method :cloaker_
|
|
602
|
+
meth
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# This will resolve the given options against the defaults.
|
|
607
|
+
# Normally just used internally.
|
|
608
|
+
def resolve_defaults(options)
|
|
609
|
+
options.merge(@defaults)
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
# Starts a listener block. This is the only one that actually takes
|
|
613
|
+
# a block and then you make Configurator.uri calls in order to setup
|
|
614
|
+
# your URIs and handlers. If you write your Handlers as GemPlugins
|
|
615
|
+
# then you can use load_plugins and plugin to load them.
|
|
616
|
+
#
|
|
617
|
+
# It expects the following options (or defaults):
|
|
618
|
+
#
|
|
619
|
+
# * :host => Host name to bind.
|
|
620
|
+
# * :port => Port to bind.
|
|
621
|
+
# * :num_processors => The maximum number of concurrent threads allowed. (950 default)
|
|
622
|
+
# * :timeout => 1/100th of a second timeout between requests. (10 is 1/10th, 0 is not timeout)
|
|
623
|
+
#
|
|
624
|
+
def listener(options={},&blk)
|
|
625
|
+
ops = resolve_defaults(options)
|
|
626
|
+
ops[:num_processors] ||= 950
|
|
627
|
+
ops[:timeout] ||= 0
|
|
628
|
+
|
|
629
|
+
@listener = Mongrel::HttpServer.new(ops[:host], ops[:port].to_i, ops[:num_processors].to_i, ops[:timeout].to_i)
|
|
630
|
+
@listener_name = "#{ops[:host]}:#{ops[:port]}"
|
|
631
|
+
@listeners[@listener_name] = @listener
|
|
632
|
+
|
|
633
|
+
if blk
|
|
634
|
+
cloaker(&blk).bind(self).call
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
# all done processing this listener setup
|
|
638
|
+
@listener = nil
|
|
639
|
+
@listener_name = nil
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
# Called inside a Configurator.listener block in order to
|
|
644
|
+
# add URI->handler mappings for that listener. Use this as
|
|
645
|
+
# many times as you like. It expects the following options
|
|
646
|
+
# or defaults:
|
|
647
|
+
#
|
|
648
|
+
# * :handler => Handler to use for this location.
|
|
649
|
+
def uri(location, options={})
|
|
650
|
+
ops = resolve_defaults(options)
|
|
651
|
+
@listener.register(location, ops[:handler])
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
# Daemonizes the current Ruby script turning all the
|
|
656
|
+
# listeners into an actual "server" or detached process.
|
|
657
|
+
# You must call this *before* frameworks that open files
|
|
658
|
+
# as otherwise the files will be closed by this function.
|
|
659
|
+
#
|
|
660
|
+
# Does not work for Win32 systems (the call is silently ignored).
|
|
661
|
+
#
|
|
662
|
+
# Requires the following options or defaults:
|
|
663
|
+
#
|
|
664
|
+
# * :cwd => Directory to change to.
|
|
665
|
+
# * :log_file => Where to write STDOUT and STDERR.
|
|
666
|
+
# * :pid_file => Where to write the process ID.
|
|
667
|
+
#
|
|
668
|
+
# It is safe to call this on win32 as it will only require daemons
|
|
669
|
+
# if NOT win32.
|
|
670
|
+
def daemonize(options={})
|
|
671
|
+
ops = resolve_defaults(options)
|
|
672
|
+
# save this for later since daemonize will hose it
|
|
673
|
+
if RUBY_PLATFORM !~ /mswin/
|
|
674
|
+
require 'daemons/daemonize'
|
|
675
|
+
|
|
676
|
+
Daemonize.daemonize(log_file=File.join(ops[:cwd], ops[:log_file]))
|
|
677
|
+
|
|
678
|
+
# change back to the original starting directory
|
|
679
|
+
Dir.chdir(ops[:cwd])
|
|
680
|
+
|
|
681
|
+
open(ops[:pid_file],"w") {|f| f.write(Process.pid) }
|
|
682
|
+
else
|
|
683
|
+
log "WARNING: Win32 does not support daemon mode."
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
# Uses the GemPlugin system to easily load plugins based on their
|
|
689
|
+
# gem dependencies. You pass in either an :includes => [] or
|
|
690
|
+
# :excludes => [] setting listing the names of plugins to include
|
|
691
|
+
# or exclude from the loading.
|
|
692
|
+
def load_plugins(options={})
|
|
693
|
+
ops = resolve_defaults(options)
|
|
694
|
+
|
|
695
|
+
load_settings = {}
|
|
696
|
+
if ops[:includes]
|
|
697
|
+
ops[:includes].each do |plugin|
|
|
698
|
+
load_settings[plugin] = GemPlugin::INCLUDE
|
|
699
|
+
end
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
if ops[:excludes]
|
|
703
|
+
ops[:excludes].each do |plugin|
|
|
704
|
+
load_settings[plugin] = GemPlugin::EXCLUDE
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
GemPlugin::Manager.instance.load(load_settings)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
# Easy way to load a YAML file and apply default settings.
|
|
713
|
+
def load_yaml(file, default={})
|
|
714
|
+
default.merge(YAML.load_file(file))
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
# Loads the MIME map file and checks that it is correct
|
|
719
|
+
# on loading. This is commonly passed to Mongrel::DirHandler
|
|
720
|
+
# or any framework handler that uses DirHandler to serve files.
|
|
721
|
+
# You can also include a set of default MIME types as additional
|
|
722
|
+
# settings. See Mongrel::DirHandler for how the MIME types map
|
|
723
|
+
# is organized.
|
|
724
|
+
def load_mime_map(file, mime={})
|
|
725
|
+
# configure any requested mime map
|
|
726
|
+
log "Loading additional MIME types from #{file}"
|
|
727
|
+
mime = load_yaml(file, mime)
|
|
728
|
+
|
|
729
|
+
# check all the mime types to make sure they are the right format
|
|
730
|
+
mime.each {|k,v| log "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
|
|
731
|
+
|
|
732
|
+
return mime
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
# Loads and creates a plugin for you based on the given
|
|
737
|
+
# name and configured with the selected options. The options
|
|
738
|
+
# are merged with the defaults prior to passing them in.
|
|
739
|
+
def plugin(name, options={})
|
|
740
|
+
ops = resolve_defaults(options)
|
|
741
|
+
GemPlugin::Manager.instance.create(name, ops)
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
# Works like a meta run method which goes through all the
|
|
746
|
+
# configured listeners. Use the Configurator.join method
|
|
747
|
+
# to prevent Ruby from exiting until each one is done.
|
|
748
|
+
def run
|
|
749
|
+
@listeners.each {|name,s|
|
|
750
|
+
log "Running #{name} listener."
|
|
751
|
+
s.run
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
# Calls .stop on all the configured listeners so they
|
|
757
|
+
# stop processing requests (gracefully).
|
|
758
|
+
def stop
|
|
759
|
+
@listeners.each {|name,s|
|
|
760
|
+
log "Stopping #{name} listener."
|
|
761
|
+
s.stop
|
|
762
|
+
}
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
# This method should actually be called *outside* of the
|
|
767
|
+
# Configurator block so that you can control it. In otherwords
|
|
768
|
+
# do it like: config.join.
|
|
769
|
+
def join
|
|
770
|
+
@listeners.values.each {|s| s.acceptor.join }
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
# Calling this before you register your URIs to the given location
|
|
775
|
+
# will setup a set of handlers that log open files, objects, and the
|
|
776
|
+
# parameters for each request. This helps you track common problems
|
|
777
|
+
# found in Rails applications that are either slow or become unresponsive
|
|
778
|
+
# after a little while.
|
|
779
|
+
def debug(location)
|
|
780
|
+
require 'mongrel/debug'
|
|
781
|
+
ObjectTracker.configure
|
|
782
|
+
MongrelDbg.configure
|
|
783
|
+
MongrelDbg.begin_trace :objects
|
|
784
|
+
MongrelDbg.begin_trace :rails
|
|
785
|
+
MongrelDbg.begin_trace :files
|
|
786
|
+
|
|
787
|
+
uri location, :handler => plugin("/handlers/requestlog::files")
|
|
788
|
+
uri location, :handler => plugin("/handlers/requestlog::objects")
|
|
789
|
+
uri location, :handler => plugin("/handlers/requestlog::params")
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
# Sets up the standard signal handlers that are used on most Ruby
|
|
794
|
+
# It only configures if the platform is not win32 and doesn't do
|
|
795
|
+
# a HUP signal since this is typically framework specific.
|
|
796
|
+
#
|
|
797
|
+
# Requires a :pid_file option to indicate a file to delete.
|
|
798
|
+
# It sets the MongrelConfig.needs_restart attribute if
|
|
799
|
+
# the start command should reload. It's up to you to detect this
|
|
800
|
+
# and do whatever is needed for a "restart".
|
|
801
|
+
#
|
|
802
|
+
# This command is safely ignored if the platform is win32 (with a warning)
|
|
803
|
+
def setup_signals(options={})
|
|
804
|
+
ops = resolve_defaults(options)
|
|
805
|
+
|
|
806
|
+
if RUBY_PLATFORM !~ /mswin/
|
|
807
|
+
# graceful shutdown
|
|
808
|
+
trap("TERM") {
|
|
809
|
+
log "TERM signal received."
|
|
810
|
+
stop
|
|
811
|
+
File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
# restart
|
|
815
|
+
trap("USR2") {
|
|
816
|
+
log "USR2 signal received."
|
|
817
|
+
stop
|
|
818
|
+
File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
|
|
819
|
+
@needs_restart = true
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
trap("INT") {
|
|
823
|
+
log "INT signal received."
|
|
824
|
+
stop
|
|
825
|
+
File.unlink ops[:pid_file] if File.exist?(ops[:pid_file])
|
|
826
|
+
@needs_restart = false
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
log "Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart)."
|
|
830
|
+
else
|
|
831
|
+
log "WARNING: Win32 does not have signals support."
|
|
832
|
+
end
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
# Logs a simple message to STDERR (or the mongrel log if in daemon mode).
|
|
836
|
+
def log(msg)
|
|
837
|
+
STDERR.print "** ", msg, "\n"
|
|
838
|
+
end
|
|
839
|
+
|
|
477
840
|
end
|
|
478
841
|
|
|
479
842
|
end
|