engineyard-mongrel 1.1.4

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 +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