mongrel 0.3.11 → 0.3.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|