engineyard-mongrel 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/CHANGELOG +16 -0
  2. data/CONTRIBUTORS +17 -0
  3. data/COPYING +55 -0
  4. data/LICENSE +55 -0
  5. data/Manifest +71 -0
  6. data/README +74 -0
  7. data/bin/mongrel_rails +285 -0
  8. data/examples/builder.rb +29 -0
  9. data/examples/camping/README +3 -0
  10. data/examples/camping/blog.rb +294 -0
  11. data/examples/camping/tepee.rb +149 -0
  12. data/examples/httpd.conf +474 -0
  13. data/examples/mime.yaml +3 -0
  14. data/examples/mongrel.conf +9 -0
  15. data/examples/mongrel_simple_ctrl.rb +92 -0
  16. data/examples/mongrel_simple_service.rb +116 -0
  17. data/examples/monitrc +57 -0
  18. data/examples/random_thrash.rb +19 -0
  19. data/examples/simpletest.rb +52 -0
  20. data/examples/webrick_compare.rb +20 -0
  21. data/ext/http11/ext_help.h +15 -0
  22. data/ext/http11/extconf.rb +6 -0
  23. data/ext/http11/http11.c +527 -0
  24. data/ext/http11/http11_parser.c +1243 -0
  25. data/ext/http11/http11_parser.h +49 -0
  26. data/ext/http11/http11_parser.java.rl +171 -0
  27. data/ext/http11/http11_parser.rl +152 -0
  28. data/ext/http11/http11_parser_common.rl +54 -0
  29. data/ext/http11_java/Http11Service.java +13 -0
  30. data/ext/http11_java/org/jruby/mongrel/Http11.java +266 -0
  31. data/ext/http11_java/org/jruby/mongrel/Http11Parser.java +474 -0
  32. data/lib/mongrel.rb +360 -0
  33. data/lib/mongrel/camping.rb +107 -0
  34. data/lib/mongrel/cgi.rb +181 -0
  35. data/lib/mongrel/command.rb +222 -0
  36. data/lib/mongrel/configurator.rb +389 -0
  37. data/lib/mongrel/const.rb +110 -0
  38. data/lib/mongrel/debug.rb +203 -0
  39. data/lib/mongrel/gems.rb +22 -0
  40. data/lib/mongrel/handlers.rb +468 -0
  41. data/lib/mongrel/header_out.rb +28 -0
  42. data/lib/mongrel/http_request.rb +155 -0
  43. data/lib/mongrel/http_response.rb +166 -0
  44. data/lib/mongrel/init.rb +10 -0
  45. data/lib/mongrel/mime_types.yml +616 -0
  46. data/lib/mongrel/rails.rb +214 -0
  47. data/lib/mongrel/stats.rb +89 -0
  48. data/lib/mongrel/tcphack.rb +18 -0
  49. data/lib/mongrel/uri_classifier.rb +76 -0
  50. data/mongrel-public_cert.pem +20 -0
  51. data/setup.rb +1585 -0
  52. data/test/benchmark/previous.rb +11 -0
  53. data/test/benchmark/simple.rb +11 -0
  54. data/test/benchmark/utils.rb +82 -0
  55. data/test/mime.yaml +3 -0
  56. data/test/mongrel.conf +1 -0
  57. data/test/test_helper.rb +79 -0
  58. data/test/tools/trickletest.rb +45 -0
  59. data/test/unit/test_cgi_wrapper.rb +26 -0
  60. data/test/unit/test_command.rb +86 -0
  61. data/test/unit/test_conditional.rb +107 -0
  62. data/test/unit/test_configurator.rb +88 -0
  63. data/test/unit/test_debug.rb +25 -0
  64. data/test/unit/test_handlers.rb +136 -0
  65. data/test/unit/test_http_parser.rb +156 -0
  66. data/test/unit/test_redirect_handler.rb +45 -0
  67. data/test/unit/test_request_progress.rb +100 -0
  68. data/test/unit/test_response.rb +127 -0
  69. data/test/unit/test_stats.rb +35 -0
  70. data/test/unit/test_uriclassifier.rb +261 -0
  71. data/test/unit/test_ws.rb +116 -0
  72. metadata +158 -0
@@ -0,0 +1,11 @@
1
+ # Benchmark to compare Mongrel performance against
2
+ # previous Mongrel version (the one installed as a gem).
3
+ #
4
+ # Run with:
5
+ #
6
+ # ruby previous.rb [num of request]
7
+ #
8
+
9
+ require File.dirname(__FILE__) + '/utils'
10
+
11
+ benchmark "print", %w(current gem), 1000, [1, 10, 100]
@@ -0,0 +1,11 @@
1
+ #
2
+ # Simple benchmark to compare Mongrel performance against
3
+ # other webservers supported by Rack.
4
+ #
5
+
6
+ require File.dirname(__FILE__) + '/utils'
7
+
8
+ libs = %w(current gem WEBrick EMongrel Thin)
9
+ libs = ARGV if ARGV.any?
10
+
11
+ benchmark "print", libs, 1000, [1, 10, 100]
@@ -0,0 +1,82 @@
1
+
2
+ require 'rubygems'
3
+ require 'rack'
4
+ require 'rack/lobster'
5
+
6
+ def run(handler_name, n=1000, c=1)
7
+ port = 7000
8
+
9
+ server = fork do
10
+ [STDOUT, STDERR].each { |o| o.reopen "/dev/null" }
11
+
12
+ case handler_name
13
+ when 'EMongrel'
14
+ require 'swiftcore/evented_mongrel'
15
+ handler_name = 'Mongrel'
16
+
17
+ when 'Thin'
18
+ require 'thin'
19
+ hander_name = 'Thin'
20
+
21
+ when 'gem' # Load the current Mongrel gem
22
+ require 'mongrel'
23
+ handler_name = 'Mongrel'
24
+
25
+ when 'current' # Load the current Mongrel version under /lib
26
+ require File.dirname(__FILE__) + '/../lib/mongrel'
27
+ handler_name = 'Mongrel'
28
+
29
+ end
30
+
31
+ app = Rack::Lobster.new
32
+
33
+ handler = Rack::Handler.const_get(handler_name)
34
+ handler.run app, :Host => '0.0.0.0', :Port => port
35
+ end
36
+
37
+ sleep 2
38
+
39
+ out = `nice -n20 ab -c #{c} -n #{n} http://127.0.0.1:#{port}/ 2> /dev/null`
40
+
41
+ Process.kill('SIGKILL', server)
42
+ Process.wait
43
+
44
+ if requests = out.match(/^Requests.+?(\d+\.\d+)/)
45
+ requests[1].to_i
46
+ else
47
+ 0
48
+ end
49
+ end
50
+
51
+ def benchmark(type, servers, request, concurrency_levels)
52
+ send "#{type}_benchmark", servers, request, concurrency_levels
53
+ end
54
+
55
+ def graph_benchmark(servers, request, concurrency_levels)
56
+ require '/usr/local/lib/ruby/gems/1.8/gems/gruff-0.2.9/lib/gruff'
57
+ g = Gruff::Area.new
58
+ g.title = "Server benchmark"
59
+
60
+ servers.each do |server|
61
+ g.data(server, concurrency_levels.collect { |c| print '.'; run(server, request, c) })
62
+ end
63
+ puts
64
+
65
+ g.x_axis_label = 'Concurrency'
66
+ g.y_axis_label = 'Requests / sec'
67
+ g.labels = {}
68
+ concurrency_levels.each_with_index { |c, i| g.labels[i] = c.to_s }
69
+
70
+ g.write('bench.png')
71
+ `open bench.png`
72
+ end
73
+
74
+ def print_benchmark(servers, request, concurrency_levels)
75
+ puts 'server request concurrency req/s'
76
+ puts '=' * 42
77
+ concurrency_levels.each do |c|
78
+ servers.each do |server|
79
+ puts "#{server.ljust(8)} #{request} #{c.to_s.ljust(4)} #{run(server, request, c)}"
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ .jpeg: image/jpeg
3
+ .png: image/test
@@ -0,0 +1 @@
1
+ uri "/fromconf", :handler => Mongrel::Error404Handler.new("test")
@@ -0,0 +1,79 @@
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
75
+
76
+ # Platform check helper ;-)
77
+ def windows?
78
+ result = RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/
79
+ end
@@ -0,0 +1,45 @@
1
+ require 'socket'
2
+ require 'stringio'
3
+
4
+ def do_test(st, chunk)
5
+ s = TCPSocket.new('127.0.0.1',ARGV[0].to_i);
6
+ req = StringIO.new(st)
7
+ nout = 0
8
+ randstop = rand(st.length / 10)
9
+ STDERR.puts "stopping after: #{randstop}"
10
+
11
+ begin
12
+ while data = req.read(chunk)
13
+ nout += s.write(data)
14
+ s.flush
15
+ sleep 0.1
16
+ if nout > randstop
17
+ STDERR.puts "BANG! after #{nout} bytes."
18
+ break
19
+ end
20
+ end
21
+ rescue Object => e
22
+ STDERR.puts "ERROR: #{e}"
23
+ ensure
24
+ s.close
25
+ end
26
+ end
27
+
28
+ content = "-" * (1024 * 240)
29
+ st = "GET / HTTP/1.1\r\nHost: www.zedshaw.com\r\nContent-Type: text/plain\r\nContent-Length: #{content.length}\r\n\r\n#{content}"
30
+
31
+ puts "length: #{content.length}"
32
+
33
+ threads = []
34
+ ARGV[1].to_i.times do
35
+ t = Thread.new do
36
+ size = 100
37
+ puts ">>>> #{size} sized chunks"
38
+ do_test(st, size)
39
+ end
40
+
41
+ t.abort_on_exception = true
42
+ threads << t
43
+ end
44
+
45
+ threads.each {|t| t.join}
@@ -0,0 +1,26 @@
1
+
2
+ require 'test/test_helper'
3
+
4
+ class MockHttpRequest
5
+ attr_reader :body
6
+
7
+ def params
8
+ return { 'REQUEST_METHOD' => 'GET'}
9
+ end
10
+ end
11
+
12
+ class CGIWrapperTest < Test::Unit::TestCase
13
+
14
+ def test_set_cookies_output_cookies
15
+ request = MockHttpRequest.new
16
+ response = nil # not needed for this test
17
+ output_headers = {}
18
+
19
+ cgi = Mongrel::CGIWrapper.new(request, response)
20
+ session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::MemoryStore)
21
+ cgi.send_cookies(output_headers)
22
+
23
+ assert(output_headers.has_key?("Set-Cookie"))
24
+ assert_equal("_session_id="+session.session_id+"; path=", output_headers["Set-Cookie"])
25
+ end
26
+ end
@@ -0,0 +1,86 @@
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/test_helper'
8
+
9
+ class TestCommand < GemPlugin::Plugin "/commands"
10
+ include Mongrel::Command::Base
11
+
12
+ def configure
13
+ options [
14
+ ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
15
+ ['', '--user USER', "User to run as", :@user, nil],
16
+ ["-d", "--daemonize", "Whether to run in the background or not", :@daemon, false],
17
+ ["-x", "--test", "Used to let the test run failures", :@test, false],
18
+ ]
19
+ end
20
+
21
+ def validate
22
+ valid_dir? ".", "Can't validate current directory."
23
+ valid_exists? "Rakefile", "Rakefile not there, test is invalid."
24
+ if @test
25
+ valid_exist? "BADFILE", "Yeah, badfile"
26
+ valid_file? "BADFILE", "Not even a file"
27
+ valid_dir? "BADDIR", "No dir here"
28
+ valid? false, "Total failure"
29
+ end
30
+
31
+ return @valid
32
+ end
33
+
34
+
35
+ def run
36
+ $test_command_ran = true
37
+ end
38
+ end
39
+
40
+ class CommandTest < Test::Unit::TestCase
41
+
42
+ def setup
43
+ $test_command_ran = false
44
+ end
45
+
46
+ def teardown
47
+ end
48
+
49
+ def run_cmd(args)
50
+ Mongrel::Command::Registry.instance.run args
51
+ end
52
+
53
+ def test_run_command
54
+ redirect_test_io do
55
+ run_cmd ["testcommand"]
56
+ assert $test_command_ran, "command didn't run"
57
+ end
58
+ end
59
+
60
+ def test_command_error
61
+ redirect_test_io do
62
+ run_cmd ["crapcommand"]
63
+ end
64
+ end
65
+
66
+ def test_command_listing
67
+ redirect_test_io do
68
+ run_cmd ["help"]
69
+ end
70
+ end
71
+
72
+ def test_options
73
+ redirect_test_io do
74
+ run_cmd ["testcommand","-h"]
75
+ run_cmd ["testcommand","--help"]
76
+ run_cmd ["testcommand","-e","test","-d","--user"]
77
+ end
78
+ end
79
+
80
+ def test_version
81
+ redirect_test_io do
82
+ run_cmd ["testcommand", "--version"]
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,107 @@
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/test_helper'
8
+
9
+ include Mongrel
10
+
11
+ class ConditionalResponseTest < Test::Unit::TestCase
12
+ def setup
13
+ @server = HttpServer.new('127.0.0.1', process_based_port)
14
+ @server.register('/', Mongrel::DirHandler.new('.'))
15
+ @server.run
16
+
17
+ @http = Net::HTTP.new(@server.host, @server.port)
18
+
19
+ # get the ETag and Last-Modified headers
20
+ @path = '/README'
21
+ res = @http.start { |http| http.get(@path) }
22
+ assert_not_nil @etag = res['ETag']
23
+ assert_not_nil @last_modified = res['Last-Modified']
24
+ assert_not_nil @content_length = res['Content-Length']
25
+ end
26
+
27
+ def teardown
28
+ @server.stop(true)
29
+ end
30
+
31
+ # status should be 304 Not Modified when If-None-Match is the matching ETag
32
+ def test_not_modified_via_if_none_match
33
+ assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag
34
+ end
35
+
36
+ # status should be 304 Not Modified when If-Modified-Since is the matching Last-Modified date
37
+ def test_not_modified_via_if_modified_since
38
+ assert_status_for_get_and_head Net::HTTPNotModified, 'If-Modified-Since' => @last_modified
39
+ end
40
+
41
+ # status should be 304 Not Modified when If-None-Match is the matching ETag
42
+ # and If-Modified-Since is the matching Last-Modified date
43
+ def test_not_modified_via_if_none_match_and_if_modified_since
44
+ assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => @last_modified
45
+ end
46
+
47
+ # status should be 200 OK when If-None-Match is invalid
48
+ def test_invalid_if_none_match
49
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid'
50
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => 'invalid', 'If-Modified-Since' => @last_modified
51
+ end
52
+
53
+ # status should be 200 OK when If-Modified-Since is invalid
54
+ def test_invalid_if_modified_since
55
+ assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => 'invalid'
56
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => 'invalid'
57
+ end
58
+
59
+ # status should be 304 Not Modified when If-Modified-Since is greater than the Last-Modified header, but less than the system time
60
+ def test_if_modified_since_greater_than_last_modified
61
+ sleep 2
62
+ last_modified_plus_1 = (Time.httpdate(@last_modified) + 1).httpdate
63
+ assert_status_for_get_and_head Net::HTTPNotModified, 'If-Modified-Since' => last_modified_plus_1
64
+ assert_status_for_get_and_head Net::HTTPNotModified, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_plus_1
65
+ end
66
+
67
+ # status should be 200 OK when If-Modified-Since is less than the Last-Modified header
68
+ def test_if_modified_since_less_than_last_modified
69
+ last_modified_minus_1 = (Time.httpdate(@last_modified) - 1).httpdate
70
+ assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => last_modified_minus_1
71
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => last_modified_minus_1
72
+ end
73
+
74
+ # status should be 200 OK when If-Modified-Since is a date in the future
75
+ def test_future_if_modified_since
76
+ the_future = Time.at(2**31-1).httpdate
77
+ assert_status_for_get_and_head Net::HTTPOK, 'If-Modified-Since' => the_future
78
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => @etag, 'If-Modified-Since' => the_future
79
+ end
80
+
81
+ # status should be 200 OK when If-None-Match is a wildcard
82
+ def test_wildcard_match
83
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*'
84
+ assert_status_for_get_and_head Net::HTTPOK, 'If-None-Match' => '*', 'If-Modified-Since' => @last_modified
85
+ end
86
+
87
+ private
88
+
89
+ # assert the response status is correct for GET and HEAD
90
+ def assert_status_for_get_and_head(response_class, headers = {})
91
+ %w{ get head }.each do |method|
92
+ res = @http.send(method, @path, headers)
93
+ assert_kind_of response_class, res
94
+ assert_equal @etag, res['ETag']
95
+ case response_class.to_s
96
+ when 'Net::HTTPNotModified' then
97
+ assert_nil res['Last-Modified']
98
+ assert_nil res['Content-Length']
99
+ when 'Net::HTTPOK' then
100
+ assert_equal @last_modified, res['Last-Modified']
101
+ assert_equal @content_length, res['Content-Length']
102
+ else
103
+ fail "Incorrect response class: #{response_class}"
104
+ end
105
+ end
106
+ end
107
+ end