freels-mongrel 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. data/CHANGELOG +12 -0
  2. data/COPYING +55 -0
  3. data/LICENSE +55 -0
  4. data/Manifest +70 -0
  5. data/README +74 -0
  6. data/Rakefile +237 -0
  7. data/TODO +4 -0
  8. data/bin/mongrel_rails +284 -0
  9. data/examples/builder.rb +29 -0
  10. data/examples/camping/README +3 -0
  11. data/examples/camping/blog.rb +294 -0
  12. data/examples/camping/tepee.rb +149 -0
  13. data/examples/httpd.conf +474 -0
  14. data/examples/mime.yaml +3 -0
  15. data/examples/mongrel.conf +9 -0
  16. data/examples/mongrel_simple_ctrl.rb +92 -0
  17. data/examples/mongrel_simple_service.rb +116 -0
  18. data/examples/monitrc +57 -0
  19. data/examples/random_thrash.rb +19 -0
  20. data/examples/simpletest.rb +52 -0
  21. data/examples/webrick_compare.rb +20 -0
  22. data/ext/http11/ext_help.h +15 -0
  23. data/ext/http11/extconf.rb +6 -0
  24. data/ext/http11/http11.c +527 -0
  25. data/ext/http11/http11_parser.c +1216 -0
  26. data/ext/http11/http11_parser.h +49 -0
  27. data/ext/http11/http11_parser.java.rl +171 -0
  28. data/ext/http11/http11_parser.rl +165 -0
  29. data/ext/http11/http11_parser_common.rl +55 -0
  30. data/ext/http11_java/Http11Service.java +13 -0
  31. data/ext/http11_java/org/jruby/mongrel/Http11.java +266 -0
  32. data/ext/http11_java/org/jruby/mongrel/Http11Parser.java +572 -0
  33. data/lib/mongrel.rb +359 -0
  34. data/lib/mongrel/camping.rb +107 -0
  35. data/lib/mongrel/cgi.rb +182 -0
  36. data/lib/mongrel/command.rb +220 -0
  37. data/lib/mongrel/configurator.rb +389 -0
  38. data/lib/mongrel/const.rb +114 -0
  39. data/lib/mongrel/debug.rb +203 -0
  40. data/lib/mongrel/gems.rb +22 -0
  41. data/lib/mongrel/handlers.rb +472 -0
  42. data/lib/mongrel/header_out.rb +28 -0
  43. data/lib/mongrel/http_request.rb +155 -0
  44. data/lib/mongrel/http_response.rb +163 -0
  45. data/lib/mongrel/init.rb +10 -0
  46. data/lib/mongrel/logger.rb +74 -0
  47. data/lib/mongrel/mime_types.yml +616 -0
  48. data/lib/mongrel/rails.rb +185 -0
  49. data/lib/mongrel/stats.rb +89 -0
  50. data/lib/mongrel/tcphack.rb +18 -0
  51. data/lib/mongrel/uri_classifier.rb +76 -0
  52. data/mongrel-public_cert.pem +20 -0
  53. data/mongrel.gemspec +47 -0
  54. data/setup.rb +1585 -0
  55. data/test/mime.yaml +3 -0
  56. data/test/mongrel.conf +1 -0
  57. data/test/test_cgi_wrapper.rb +26 -0
  58. data/test/test_command.rb +86 -0
  59. data/test/test_conditional.rb +107 -0
  60. data/test/test_configurator.rb +88 -0
  61. data/test/test_debug.rb +25 -0
  62. data/test/test_handlers.rb +104 -0
  63. data/test/test_http11.rb +272 -0
  64. data/test/test_redirect_handler.rb +44 -0
  65. data/test/test_request_progress.rb +100 -0
  66. data/test/test_response.rb +127 -0
  67. data/test/test_stats.rb +35 -0
  68. data/test/test_uriclassifier.rb +261 -0
  69. data/test/test_ws.rb +116 -0
  70. data/test/testhelp.rb +74 -0
  71. data/tools/trickletest.rb +45 -0
  72. metadata +202 -0
@@ -0,0 +1,35 @@
1
+ # Copyright (c) 2005 Zed A. Shaw
2
+ # You can redistribute it and/or modify it under the same terms as Ruby.
3
+ #
4
+ # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
5
+ # for more information.
6
+
7
+ require 'test/testhelp'
8
+
9
+ class StatsTest < Test::Unit::TestCase
10
+
11
+ def test_sampling_speed
12
+ out = StringIO.new
13
+
14
+ s = Mongrel::Stats.new("test")
15
+ t = Mongrel::Stats.new("time")
16
+
17
+ 100.times { s.sample(rand(20)); t.tick }
18
+
19
+ s.dump("FIRST", out)
20
+ t.dump("FIRST", out)
21
+
22
+ old_mean = s.mean
23
+ old_sd = s.sd
24
+
25
+ s.reset
26
+ t.reset
27
+ 100.times { s.sample(rand(30)); t.tick }
28
+
29
+ s.dump("SECOND", out)
30
+ t.dump("SECOND", out)
31
+ assert_not_equal old_mean, s.mean
32
+ assert_not_equal old_mean, s.sd
33
+ end
34
+
35
+ end
@@ -0,0 +1,261 @@
1
+ # Copyright (c) 2005 Zed A. Shaw
2
+ # You can redistribute it and/or modify it under the same terms as Ruby.
3
+ #
4
+ # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
5
+ # for more information.
6
+
7
+ require 'test/testhelp'
8
+
9
+ include Mongrel
10
+
11
+ class URIClassifierTest < Test::Unit::TestCase
12
+
13
+ def test_uri_finding
14
+ uri_classifier = URIClassifier.new
15
+ uri_classifier.register("/test", 1)
16
+
17
+ script_name, path_info, value = uri_classifier.resolve("/test")
18
+ assert_equal 1, value
19
+ assert_equal "/test", script_name
20
+ end
21
+
22
+ def test_root_handler_only
23
+ uri_classifier = URIClassifier.new
24
+ uri_classifier.register("/", 1)
25
+
26
+ script_name, path_info, value = uri_classifier.resolve("/test")
27
+ assert_equal 1, value
28
+ assert_equal "/", script_name
29
+ assert_equal "/test", path_info
30
+ end
31
+
32
+ def test_uri_prefix_ops
33
+ test = "/pre/fix/test"
34
+ prefix = "/pre"
35
+
36
+ uri_classifier = URIClassifier.new
37
+ uri_classifier.register(prefix,1)
38
+
39
+ script_name, path_info, value = uri_classifier.resolve(prefix)
40
+ script_name, path_info, value = uri_classifier.resolve(test)
41
+ assert_equal 1, value
42
+ assert_equal prefix, script_name
43
+ assert_equal test[script_name.length .. -1], path_info
44
+
45
+ assert uri_classifier.inspect
46
+ assert_equal prefix, uri_classifier.uris[0]
47
+ end
48
+
49
+ def test_not_finding
50
+ test = "/cant/find/me"
51
+ uri_classifier = URIClassifier.new
52
+ uri_classifier.register(test, 1)
53
+
54
+ script_name, path_info, value = uri_classifier.resolve("/nope/not/here")
55
+ assert_nil script_name
56
+ assert_nil path_info
57
+ assert_nil value
58
+ end
59
+
60
+ def test_exceptions
61
+ uri_classifier = URIClassifier.new
62
+
63
+ uri_classifier.register("/test", 1)
64
+
65
+ failed = false
66
+ begin
67
+ uri_classifier.register("/test", 1)
68
+ rescue => e
69
+ failed = true
70
+ end
71
+
72
+ assert failed
73
+
74
+ failed = false
75
+ begin
76
+ uri_classifier.register("", 1)
77
+ rescue => e
78
+ failed = true
79
+ end
80
+
81
+ assert failed
82
+ end
83
+
84
+
85
+ def test_register_unregister
86
+ uri_classifier = URIClassifier.new
87
+
88
+ 100.times do
89
+ uri_classifier.register("/stuff", 1)
90
+ value = uri_classifier.unregister("/stuff")
91
+ assert_equal 1, value
92
+ end
93
+
94
+ uri_classifier.register("/things",1)
95
+ script_name, path_info, value = uri_classifier.resolve("/things")
96
+ assert_equal 1, value
97
+
98
+ uri_classifier.unregister("/things")
99
+ script_name, path_info, value = uri_classifier.resolve("/things")
100
+ assert_nil value
101
+
102
+ end
103
+
104
+
105
+ def test_uri_branching
106
+ uri_classifier = URIClassifier.new
107
+ uri_classifier.register("/test", 1)
108
+ uri_classifier.register("/test/this",2)
109
+
110
+ script_name, path_info, handler = uri_classifier.resolve("/test")
111
+ script_name, path_info, handler = uri_classifier.resolve("/test/that")
112
+ assert_equal "/test", script_name, "failed to properly find script off branch portion of uri"
113
+ assert_equal "/that", path_info
114
+ assert_equal 1, handler, "wrong result for branching uri"
115
+ end
116
+
117
+ def test_all_prefixing
118
+ tests = ["/test","/test/that","/test/this"]
119
+ uri = "/test/this/that"
120
+ uri_classifier = URIClassifier.new
121
+
122
+ current = ""
123
+ uri.each_byte do |c|
124
+ current << c.chr
125
+ uri_classifier.register(current, c)
126
+ end
127
+
128
+
129
+ # Try to resolve everything with no asserts as a fuzzing
130
+ tests.each do |prefix|
131
+ current = ""
132
+ prefix.each_byte do |c|
133
+ current << c.chr
134
+ script_name, path_info, handler = uri_classifier.resolve(current)
135
+ assert script_name
136
+ assert path_info
137
+ assert handler
138
+ end
139
+ end
140
+
141
+ # Assert that we find stuff
142
+ tests.each do |t|
143
+ script_name, path_info, handler = uri_classifier.resolve(t)
144
+ assert handler
145
+ end
146
+
147
+ # Assert we don't find stuff
148
+ script_name, path_info, handler = uri_classifier.resolve("chicken")
149
+ assert_nil handler
150
+ assert_nil script_name
151
+ assert_nil path_info
152
+ end
153
+
154
+
155
+ # Verifies that a root mounted ("/") handler resolves
156
+ # such that path info matches the original URI.
157
+ # This is needed to accommodate real usage of handlers.
158
+ def test_root_mounted
159
+ uri_classifier = URIClassifier.new
160
+ root = "/"
161
+ path = "/this/is/a/test"
162
+
163
+ uri_classifier.register(root, 1)
164
+
165
+ script_name, path_info, handler = uri_classifier.resolve(root)
166
+ assert_equal 1, handler
167
+ assert_equal root, path_info
168
+ assert_equal root, script_name
169
+
170
+ script_name, path_info, handler = uri_classifier.resolve(path)
171
+ assert_equal path, path_info
172
+ assert_equal root, script_name
173
+ assert_equal 1, handler
174
+ end
175
+
176
+ # Verifies that a root mounted ("/") handler
177
+ # is the default point, doesn't matter the order we use
178
+ # to register the URIs
179
+ def test_classifier_order
180
+ tests = ["/before", "/way_past"]
181
+ root = "/"
182
+ path = "/path"
183
+
184
+ uri_classifier = URIClassifier.new
185
+ uri_classifier.register(path, 1)
186
+ uri_classifier.register(root, 2)
187
+
188
+ tests.each do |uri|
189
+ script_name, path_info, handler = uri_classifier.resolve(uri)
190
+ assert_equal root, script_name, "#{uri} did not resolve to #{root}"
191
+ assert_equal uri, path_info
192
+ assert_equal 2, handler
193
+ end
194
+ end
195
+
196
+ if ENV['BENCHMARK']
197
+ # Eventually we will have a suite of benchmarks instead of lamely installing a test
198
+
199
+ def test_benchmark
200
+
201
+ # This URI set should favor a TST. Both versions increase linearly until you hit 14
202
+ # URIs, then the TST flattens out.
203
+ @uris = %w(
204
+ /
205
+ /dag /dig /digbark /dog /dogbark /dog/bark /dug /dugbarking /puppy
206
+ /c /cat /cat/tree /cat/tree/mulberry /cats /cot /cot/tree/mulberry /kitty /kittycat
207
+ # /eag /eig /eigbark /eog /eogbark /eog/bark /eug /eugbarking /iuppy
208
+ # /f /fat /fat/tree /fat/tree/mulberry /fats /fot /fot/tree/mulberry /jitty /jittyfat
209
+ # /gag /gig /gigbark /gog /gogbark /gog/bark /gug /gugbarking /kuppy
210
+ # /h /hat /hat/tree /hat/tree/mulberry /hats /hot /hot/tree/mulberry /litty /littyhat
211
+ # /ceag /ceig /ceigbark /ceog /ceogbark /ceog/cbark /ceug /ceugbarking /ciuppy
212
+ # /cf /cfat /cfat/ctree /cfat/ctree/cmulberry /cfats /cfot /cfot/ctree/cmulberry /cjitty /cjittyfat
213
+ # /cgag /cgig /cgigbark /cgog /cgogbark /cgog/cbark /cgug /cgugbarking /ckuppy
214
+ # /ch /chat /chat/ctree /chat/ctree/cmulberry /chats /chot /chot/ctree/cmulberry /citty /cittyhat
215
+ )
216
+
217
+ @requests = %w(
218
+ /
219
+ /dig
220
+ /digging
221
+ /dogging
222
+ /dogbarking/
223
+ /puppy/barking
224
+ /c
225
+ /cat
226
+ /cat/shrub
227
+ /cat/tree
228
+ /cat/tree/maple
229
+ /cat/tree/mulberry/tree
230
+ /cat/tree/oak
231
+ /cats/
232
+ /cats/tree
233
+ /cod
234
+ /zebra
235
+ )
236
+
237
+ @classifier = URIClassifier.new
238
+ @uris.each do |uri|
239
+ @classifier.register(uri, 1)
240
+ end
241
+
242
+ puts "#{@uris.size} URIs / #{@requests.size * 10000} requests"
243
+
244
+ Benchmark.bm do |x|
245
+ x.report do
246
+ # require 'ruby-prof'
247
+ # profile = RubyProf.profile do
248
+ 10000.times do
249
+ @requests.each do |request|
250
+ @classifier.resolve(request)
251
+ end
252
+ end
253
+ # end
254
+ # File.open("profile.html", 'w') { |file| RubyProf::GraphHtmlPrinter.new(profile).print(file, 0) }
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ end
261
+
@@ -0,0 +1,116 @@
1
+ # Copyright (c) 2005 Zed A. Shaw
2
+ # You can redistribute it and/or modify it under the same terms as Ruby.
3
+ #
4
+ # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
5
+ # for more information.
6
+
7
+ require 'test/testhelp'
8
+
9
+ include Mongrel
10
+
11
+ class TestHandler < Mongrel::HttpHandler
12
+ attr_reader :ran_test
13
+
14
+ def process(request, response)
15
+ @ran_test = true
16
+ response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n")
17
+ end
18
+ end
19
+
20
+
21
+ class WebServerTest < Test::Unit::TestCase
22
+
23
+ def setup
24
+ @port = process_based_port
25
+ @valid_request = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\n\r\n"
26
+
27
+ redirect_test_io do
28
+ # We set num_processors=1 so that we can test the reaping code
29
+ @server = HttpServer.new("127.0.0.1", @port, num_processors=1)
30
+ end
31
+
32
+ @tester = TestHandler.new
33
+ @server.register("/test", @tester)
34
+ redirect_test_io do
35
+ @server.run
36
+ end
37
+ end
38
+
39
+ def teardown
40
+ redirect_test_io do
41
+ @server.stop(true)
42
+ end
43
+ end
44
+
45
+ def test_simple_server
46
+ hit(["http://localhost:#{@port}/test"])
47
+ assert @tester.ran_test, "Handler didn't really run"
48
+ end
49
+
50
+
51
+ def do_test(string, chunk, close_after=nil, shutdown_delay=0)
52
+ # Do not use instance variables here, because it needs to be thread safe
53
+ socket = TCPSocket.new("127.0.0.1", @port);
54
+ request = StringIO.new(string)
55
+ chunks_out = 0
56
+
57
+ while data = request.read(chunk)
58
+ chunks_out += socket.write(data)
59
+ socket.flush
60
+ sleep 0.2
61
+ if close_after and chunks_out > close_after
62
+ socket.close
63
+ sleep 1
64
+ end
65
+ end
66
+ sleep(shutdown_delay)
67
+ socket.write(" ") # Some platforms only raise the exception on attempted write
68
+ socket.flush
69
+ end
70
+
71
+ def test_trickle_attack
72
+ do_test(@valid_request, 3)
73
+ end
74
+
75
+ def test_close_client
76
+ assert_raises IOError do
77
+ do_test(@valid_request, 10, 20)
78
+ end
79
+ end
80
+
81
+ def test_bad_client
82
+ redirect_test_io do
83
+ do_test("GET /test HTTP/BAD", 3)
84
+ end
85
+ end
86
+
87
+ def test_header_is_too_long
88
+ redirect_test_io do
89
+ long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n"
90
+ assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL, IOError do
91
+ do_test(long, long.length/2, 10)
92
+ end
93
+ end
94
+ end
95
+
96
+ def test_num_processors_overload
97
+ redirect_test_io do
98
+ assert_raises Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EINVAL do
99
+ tests = [
100
+ Thread.new { do_test(@valid_request, 1) },
101
+ Thread.new { do_test(@valid_request, 10) },
102
+ ]
103
+
104
+ tests.each {|t| t.join}
105
+ end
106
+ end
107
+ end
108
+
109
+ def test_file_streamed_request
110
+ body = "a" * (Mongrel::Const::MAX_BODY * 2)
111
+ long = "GET /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body
112
+ do_test(long, Mongrel::Const::CHUNK_SIZE * 2 -400)
113
+ end
114
+
115
+ end
116
+
@@ -0,0 +1,74 @@
1
+ # Copyright (c) 2005 Zed A. Shaw
2
+ # You can redistribute it and/or modify it under the same terms as Ruby.
3
+ #
4
+ # Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
5
+ # for more information.
6
+
7
+
8
+ HERE = File.dirname(__FILE__)
9
+ %w(lib ext bin test).each do |dir|
10
+ $LOAD_PATH.unshift "#{HERE}/../#{dir}"
11
+ end
12
+
13
+ require 'rubygems'
14
+ require 'test/unit'
15
+ require 'net/http'
16
+ require 'timeout'
17
+ require 'cgi/session'
18
+ require 'fileutils'
19
+ require 'benchmark'
20
+ require 'digest/sha1'
21
+ require 'uri'
22
+ require 'stringio'
23
+ require 'pp'
24
+
25
+ require 'mongrel'
26
+ require 'mongrel/stats'
27
+
28
+ if ENV['DEBUG']
29
+ require 'ruby-debug'
30
+ Debugger.start
31
+ end
32
+
33
+ def redirect_test_io
34
+ orig_err = STDERR.dup
35
+ orig_out = STDOUT.dup
36
+ STDERR.reopen("test_stderr.log")
37
+ STDOUT.reopen("test_stdout.log")
38
+
39
+ begin
40
+ yield
41
+ ensure
42
+ STDERR.reopen(orig_err)
43
+ STDOUT.reopen(orig_out)
44
+ end
45
+ end
46
+
47
+ # Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
48
+ # HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
49
+ def hit(uris)
50
+ results = []
51
+ uris.each do |u|
52
+ res = nil
53
+
54
+ if u.kind_of? String
55
+ res = Net::HTTP.get(URI.parse(u))
56
+ else
57
+ url = URI.parse(u[0])
58
+ res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
59
+ end
60
+
61
+ assert res != nil, "Didn't get a response: #{u}"
62
+ results << res
63
+ end
64
+
65
+ return results
66
+ end
67
+
68
+ # process_based_port provides a port number, usable for TCP and UDP
69
+ # connections based on $$ and with a 5000 as base.
70
+ # this is required if you perform several builds of mongrel in parallel
71
+ # (like continuous integration systems)
72
+ def process_based_port
73
+ 5000 + $$ % 1000
74
+ end