thin 1.7.2 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +29 -107
  3. data/Gemfile +8 -0
  4. data/README.md +44 -78
  5. data/Rakefile +28 -18
  6. data/bin/thin +4 -4
  7. data/examples/async.ru +21 -0
  8. data/examples/thin.conf.rb +39 -0
  9. data/lib/thin.rb +2 -44
  10. data/lib/thin/async.rb +108 -0
  11. data/lib/thin/backends/prefork.rb +44 -0
  12. data/lib/thin/backends/single_process.rb +28 -0
  13. data/lib/thin/chunked_body.rb +28 -0
  14. data/lib/thin/configurator.rb +118 -0
  15. data/lib/thin/connection.rb +246 -172
  16. data/lib/thin/listener.rb +114 -0
  17. data/lib/thin/request.rb +94 -74
  18. data/lib/thin/response.rb +112 -45
  19. data/lib/thin/runner.rb +134 -197
  20. data/lib/thin/server.rb +203 -252
  21. data/lib/thin/system.rb +49 -0
  22. data/lib/thin/version.rb +14 -29
  23. data/man/index.txt +3 -0
  24. data/man/thin-conf.5.ronn +121 -0
  25. data/man/thin.1.ronn +105 -0
  26. data/site/.gitignore +2 -0
  27. data/site/README.md +21 -0
  28. data/site/Rakefile +20 -0
  29. data/site/config.ru +4 -0
  30. data/site/public/images/grid.png +0 -0
  31. data/site/public/javascripts/dd_belatedpng.js +13 -0
  32. data/site/public/javascripts/modernizr-1.6.min.js +30 -0
  33. data/site/public/man/thin-conf.5.html +220 -0
  34. data/site/public/man/thin.1.html +177 -0
  35. data/site/site/assets/javascripts/main.coffee +2 -0
  36. data/site/site/assets/stylesheets/_config.scss +55 -0
  37. data/site/site/assets/stylesheets/main.scss +24 -0
  38. data/site/site/helpers.rb +17 -0
  39. data/site/site/layouts/base.erb +55 -0
  40. data/site/site/layouts/default.erb +17 -0
  41. data/site/site/pages/about.md +5 -0
  42. data/site/site/pages/index.erb +10 -0
  43. data/site/site/partials/.gitkeep +0 -0
  44. data/test/fixtures/big.txt +1 -0
  45. data/test/fixtures/small.txt +1 -0
  46. data/test/fixtures/thin.conf.rb +15 -0
  47. data/test/integration/async_test.rb +35 -0
  48. data/test/integration/big_request_test.rb +30 -0
  49. data/test/integration/config.ru +57 -0
  50. data/test/integration/daemonize_test.rb +26 -0
  51. data/test/integration/env_test.rb +44 -0
  52. data/test/integration/error_test.rb +37 -0
  53. data/test/integration/file_sending_test.rb +24 -0
  54. data/test/integration/keep_alive_test.rb +35 -0
  55. data/test/integration/robustness_test.rb +37 -0
  56. data/test/integration/single_process_test.rb +15 -0
  57. data/test/integration/socket_family_test.rb +38 -0
  58. data/test/integration/worker_test.rb +22 -0
  59. data/test/test_helper.rb +195 -0
  60. data/test/unit/configurator_test.rb +43 -0
  61. data/test/unit/connection_test.rb +94 -0
  62. data/test/unit/listener_test.rb +74 -0
  63. data/test/unit/request_test.rb +74 -0
  64. data/test/unit/response_test.rb +90 -0
  65. data/test/unit/server_test.rb +29 -0
  66. data/test/unit/system_test.rb +17 -0
  67. data/thin.gemspec +26 -0
  68. data/v2.todo +21 -0
  69. metadata +134 -89
  70. checksums.yaml +0 -7
  71. data/example/adapter.rb +0 -32
  72. data/example/async_app.ru +0 -126
  73. data/example/async_chat.ru +0 -247
  74. data/example/async_tailer.ru +0 -100
  75. data/example/config.ru +0 -22
  76. data/example/monit_sockets +0 -20
  77. data/example/monit_unixsock +0 -20
  78. data/example/myapp.rb +0 -1
  79. data/example/ramaze.ru +0 -12
  80. data/example/thin.god +0 -80
  81. data/example/thin_solaris_smf.erb +0 -36
  82. data/example/thin_solaris_smf.readme.txt +0 -150
  83. data/example/vlad.rake +0 -72
  84. data/ext/thin_parser/common.rl +0 -59
  85. data/ext/thin_parser/ext_help.h +0 -14
  86. data/ext/thin_parser/extconf.rb +0 -6
  87. data/ext/thin_parser/parser.c +0 -1447
  88. data/ext/thin_parser/parser.h +0 -49
  89. data/ext/thin_parser/parser.rl +0 -152
  90. data/ext/thin_parser/thin.c +0 -435
  91. data/lib/rack/adapter/loader.rb +0 -75
  92. data/lib/rack/adapter/rails.rb +0 -178
  93. data/lib/thin/backends/base.rb +0 -167
  94. data/lib/thin/backends/swiftiply_client.rb +0 -56
  95. data/lib/thin/backends/tcp_server.rb +0 -34
  96. data/lib/thin/backends/unix_server.rb +0 -56
  97. data/lib/thin/command.rb +0 -53
  98. data/lib/thin/controllers/cluster.rb +0 -178
  99. data/lib/thin/controllers/controller.rb +0 -189
  100. data/lib/thin/controllers/service.rb +0 -76
  101. data/lib/thin/controllers/service.sh.erb +0 -39
  102. data/lib/thin/daemonizing.rb +0 -180
  103. data/lib/thin/headers.rb +0 -40
  104. data/lib/thin/logging.rb +0 -174
  105. data/lib/thin/stats.html.erb +0 -216
  106. data/lib/thin/stats.rb +0 -52
  107. data/lib/thin/statuses.rb +0 -44
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+ require "thin/configurator"
3
+
4
+ class ConfiguratorTest < Test::Unit::TestCase
5
+ def setup
6
+ @config = Thin::Configurator.new
7
+ end
8
+
9
+ def test_worker_processes
10
+ @config.worker_processes 5
11
+ assert_equal 5, @config.options[:worker_processes]
12
+ assert_raise(ArgumentError) { @config.worker_processes "5" }
13
+ end
14
+
15
+ def test_use_epoll
16
+ @config.use_epoll true
17
+ @config.use_epoll false
18
+ assert_raise(ArgumentError) { @config.use_epoll 1 }
19
+ end
20
+
21
+ def test_listen
22
+ @config.listen 3000
23
+ @config.listen "0.0.0.0:3000"
24
+ assert_raise(ArgumentError) { @config.listen false }
25
+ assert_equal 2, @config.options[:listeners].size
26
+ end
27
+
28
+ def test_before_fork
29
+ @config.before_fork { :ok }
30
+ assert_equal :ok, @config.options[:before_fork].call(:server)
31
+ end
32
+
33
+ def test_load_from_file
34
+ Thin::Configurator.load(File.expand_path("../../fixtures/thin.conf.rb", __FILE__))
35
+ end
36
+
37
+ def test_apply
38
+ server = Thin::Server.new {}
39
+ @config.worker_processes 10000
40
+ @config.apply(server)
41
+ assert_equal 10000, server.worker_processes
42
+ end
43
+ end
@@ -0,0 +1,94 @@
1
+ require 'test_helper'
2
+ require 'thin/connection'
3
+
4
+ class ConnectionTest < Test::Unit::TestCase
5
+ def setup
6
+ @connection = Thin::Connection.new(nil)
7
+ @connection.server = self
8
+ @connection.post_init
9
+
10
+ @connection.stubs(:send_data)
11
+ @connection.stubs(:close_connection_after_writing)
12
+ @connection.stubs(:socket_address).returns("127.0.0.1")
13
+ end
14
+
15
+ #### Server methods
16
+ def app
17
+ proc do |env|
18
+ [200, {"Content-Type" => "text/plain"}, ["ok"]]
19
+ end
20
+ end
21
+ def threaded?; false end
22
+ def prefork?; false end
23
+ ####
24
+
25
+ def teardown
26
+ @connection.unbind
27
+ end
28
+
29
+ def test_send_response_body
30
+ request = <<-EOS
31
+ GET / HTTP/1.1
32
+ Host: localhost:3000
33
+
34
+ EOS
35
+
36
+ @connection.expects(:send_data).with(includes("Content-Type: text/plain"))
37
+ @connection.expects(:send_data).with("ok")
38
+ @connection.expects(:close_connection_after_writing).once
39
+
40
+ @connection.receive_data(request)
41
+ end
42
+
43
+ def test_parse_get_request
44
+ request = <<-EOS
45
+ GET /path?yo=dude HTTP/1.1
46
+ Host: localhost:3000
47
+ X-Special: awesome
48
+
49
+ EOS
50
+
51
+ @connection.expects(:process)
52
+ @connection.receive_data(request)
53
+
54
+ env = @connection.request.env
55
+
56
+ assert_equal "GET", env["REQUEST_METHOD"]
57
+ assert_equal "/path", env["PATH_INFO"]
58
+ assert_equal "HTTP/1.1", env["SERVER_PROTOCOL"]
59
+ assert_equal "HTTP/1.1", env["HTTP_VERSION"]
60
+ assert_equal "yo=dude", env["QUERY_STRING"]
61
+ assert_equal "localhost:3000", env["HTTP_HOST"]
62
+ assert_equal "awesome", env["HTTP_X_SPECIAL"]
63
+
64
+ # rack. values
65
+ assert_respond_to env["rack.input"], :read
66
+ assert_equal "http", env["rack.url_scheme"]
67
+ end
68
+
69
+ def test_parse_post_request
70
+ request = <<-EOS
71
+ POST /path HTTP/1.1
72
+ Host: localhost:3000
73
+ Content-Type: text/plain
74
+ Content-Length: 2
75
+
76
+ hi
77
+ EOS
78
+
79
+ @connection.expects(:process)
80
+ @connection.receive_data(request)
81
+
82
+ env = @connection.request.env
83
+
84
+ assert_equal "POST", env["REQUEST_METHOD"]
85
+ assert_equal "/path", env["PATH_INFO"]
86
+ assert_equal "hi", env["rack.input"].read
87
+ end
88
+
89
+ def test_async_response_do_not_send_response
90
+ @connection.expects(:send_response).never
91
+
92
+ @connection.process_response(Thin::Response::ASYNC)
93
+ end
94
+ end
@@ -0,0 +1,74 @@
1
+ require 'test_helper'
2
+
3
+ class ListenerTest < Test::Unit::TestCase
4
+ def test_parse_integer_port_addresses
5
+ listener = Thin::Listener.new(3000)
6
+ assert_equal "", listener.host
7
+ assert_equal 3000, listener.port
8
+ end
9
+
10
+ def test_parse_star_port_addresses
11
+ listener = Thin::Listener.new("*:3000")
12
+ assert_equal "", listener.host
13
+ assert_equal 3000, listener.port
14
+ end
15
+
16
+ def test_parse_ipv4_and_port
17
+ listener = Thin::Listener.new("127.0.0.1:3000")
18
+ assert_equal "127.0.0.1", listener.host
19
+ assert_equal 3000, listener.port
20
+ end
21
+
22
+ def test_parse_ipv6_and_port
23
+ listener = Thin::Listener.new("[::]:3000")
24
+ assert_equal "::", listener.host
25
+ assert_equal 3000, listener.port
26
+ end
27
+
28
+ def test_parse_unix_socket
29
+ listener = Thin::Listener.new("/file.sock")
30
+ assert_nil listener.host
31
+ assert_nil listener.port
32
+ assert_equal "/file.sock", listener.socket_file
33
+ end
34
+
35
+ def test_parse_relative_unix_socket_with_prefix
36
+ listener = Thin::Listener.new("unix:file.sock")
37
+ assert_nil listener.host
38
+ assert_nil listener.port
39
+ assert_equal "file.sock", listener.socket_file
40
+ end
41
+
42
+ def test_parse_invalid_address
43
+ assert_raise(ArgumentError) { Thin::Listener.new("file.sock") }
44
+ end
45
+
46
+ def test_ipv4_socket
47
+ listener = Thin::Listener.new(3000)
48
+ assert_kind_of Socket, listener.socket
49
+ ensure
50
+ listener.close
51
+ end
52
+
53
+ def test_ipv6_socket
54
+ listener = Thin::Listener.new("[::]:3000")
55
+ assert_kind_of Socket, listener.socket
56
+ ensure
57
+ listener.close
58
+ end
59
+
60
+ def test_unix_socket
61
+ listener = Thin::Listener.new("/tmp/thin-test.sock")
62
+ assert_kind_of Socket, listener.socket
63
+ ensure
64
+ listener.close
65
+ assert ! File.exist?("tmp/thin-test.sock")
66
+ end
67
+
68
+ def test_set_socket_option
69
+ listener = Thin::Listener.new(3000, :tcp_no_delay => true)
70
+ assert listener.socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY)
71
+ ensure
72
+ listener.close
73
+ end
74
+ end
@@ -0,0 +1,74 @@
1
+ require 'test_helper'
2
+ require "thin/request"
3
+ require "rack"
4
+
5
+ class RequestTest < Test::Unit::TestCase
6
+ def setup
7
+ @request = Thin::Request.new
8
+ end
9
+
10
+ def test_env_contains_requires_rack_variables
11
+ assert_respond_to @request.env['rack.input'], :read
12
+ assert_kind_of IO, @request.env['rack.errors']
13
+ assert_equal [1, 1], @request.env['rack.version']
14
+ end
15
+
16
+ def test_convert_parser_headers_to_rack_env
17
+ @request.headers = {
18
+ "Host" => "localhost:9292",
19
+ "Connection" => "close"
20
+ }
21
+
22
+ assert_equal "localhost:9292", @request.env["HTTP_HOST"]
23
+ assert_equal "close", @request.env["HTTP_CONNECTION"]
24
+ assert ! @request.env.key?("HOST")
25
+ assert ! @request.env.key?("CONNECTION")
26
+ end
27
+
28
+ def test_do_not_prefix_content_type_and_length
29
+ @request.headers = {
30
+ "Content-Type" => "text/html",
31
+ "Content-Length" => "1"
32
+ }
33
+
34
+ assert ! @request.env.key?("HTTP_CONTENT_TYPE")
35
+ assert ! @request.env.key?("HTTP_CONTENT_LENGTH")
36
+ assert_equal "text/html", @request.env["CONTENT_TYPE"]
37
+ assert_equal "1", @request.env["CONTENT_LENGTH"]
38
+ end
39
+
40
+ def test_extracts_server_name_and_port_from_host
41
+ @request.headers = {
42
+ "Host" => "localhost:3000"
43
+ }
44
+ assert_equal "localhost", @request.env["SERVER_NAME"]
45
+ assert_equal "3000", @request.env["SERVER_PORT"]
46
+ end
47
+
48
+ def test_defaults_server_name_and_port
49
+ @request.headers = {}
50
+ assert_equal "localhost", @request.env["SERVER_NAME"]
51
+ assert_equal "80", @request.env["SERVER_PORT"]
52
+ end
53
+
54
+ def test_validate_through_rack_lint
55
+ @request.multithread = false
56
+ @request.multiprocess = false
57
+ @request.method = "GET"
58
+ @request.path = "/info"
59
+ @request.fragment = "hello"
60
+ @request.query_string = "hey=there&yo=dude"
61
+ @request.headers = {
62
+ "Host" => "localhost:9292",
63
+ "Connection" => "close",
64
+ "Content-Type" => "text/plain"
65
+ }
66
+ @request.body << "ok"
67
+
68
+ app = proc do |env|
69
+ [200, {"Content-Type" => "text/plain"}, ["ok"]]
70
+ end
71
+
72
+ assert_nothing_raised { Rack::Lint.new(app).call(@request.env) }
73
+ end
74
+ end
@@ -0,0 +1,90 @@
1
+ require 'test_helper'
2
+ require 'thin/response'
3
+
4
+ class ResponseTest < Test::Unit::TestCase
5
+ def setup
6
+ @response = Thin::Response.new
7
+ @response.headers['Content-Type'] = 'text/html'
8
+ @response.headers['Content-Length'] = '0'
9
+ @response.body = ''
10
+ @response.finish
11
+ end
12
+
13
+ def test_initialize_with_values
14
+ @response = Thin::Response.new(201, {"Content-Type" => "text/plain"}, ["hi"])
15
+ assert_equal 201, @response.status
16
+ assert_match "Content-Type: text/plain", @response.headers.to_s
17
+ assert_equal ["hi"], @response.body
18
+ end
19
+
20
+ def test_output_headers
21
+ assert_match "Content-Type: text/html", @response.headers.to_s
22
+ assert_match "Content-Length: 0", @response.headers.to_s
23
+ assert_match "Connection: close", @response.headers.to_s
24
+ end
25
+
26
+ def test_include_server_name_header
27
+ assert_match "Server: thin", @response.headers.to_s
28
+ end
29
+
30
+ def test_output_head
31
+ assert_match /^HTTP\/1.1 200 OK/, @response.head
32
+ assert_match /\r\n\r\n$/, @response.head
33
+ end
34
+
35
+ def test_allow_duplicates_on_some_headers
36
+ @response.headers['Set-Cookie'] = 'mium=7'
37
+ @response.headers['Set-Cookie'] = 'hi=there'
38
+
39
+ assert_match "Set-Cookie: mium=7", @response.head
40
+ assert_match "Set-Cookie: hi=there", @response.head
41
+ end
42
+
43
+ def test_parse_simple_header_values
44
+ @response.headers = {
45
+ 'Host' => 'localhost'
46
+ }
47
+
48
+ assert_match "Host: localhost", @response.head
49
+ end
50
+
51
+ def test_parse_multiline_header_values_in_several_headers
52
+ @response.headers = {
53
+ 'Set-Cookie' => "mium=7\nhi=there"
54
+ }
55
+
56
+ assert_match "Set-Cookie: mium=7", @response.head
57
+ assert_match "Set-Cookie: hi=there", @response.head
58
+ end
59
+
60
+ def test_ignore__nil_headers
61
+ @response.headers = nil
62
+ @response.headers = { 'Host' => 'localhost' }
63
+ @response.headers = { 'Set-Cookie' => nil }
64
+ assert_no_match /Set-Cookie/, @response.head
65
+ end
66
+
67
+ def test_body
68
+ @response.body = ['<html>', '</html>']
69
+
70
+ out = ''
71
+ @response.each { |l| out << l }
72
+ assert_match /\r\n\r\n<html><\/html>$/, out
73
+ end
74
+
75
+ def test_string_body
76
+ @response.body = '<html></html>'
77
+
78
+ out = ''
79
+ @response.each { |l| out << l }
80
+ assert_match /\r\n\r\n<html><\/html>$/, out
81
+ end
82
+
83
+ def test_close
84
+ @response.close
85
+ end
86
+
87
+ def test_async
88
+ assert Thin::Response.new(*Thin::Response::ASYNC).async?
89
+ end
90
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ class ServerTest < Test::Unit::TestCase
4
+ def setup
5
+ app = proc { |env| [200, {}, ["ok"]] }
6
+ @server = Thin::Server.new { app }
7
+ end
8
+
9
+ def test_pick_prefork_backend_if_any_workers
10
+ @server.worker_processes = 1
11
+ assert_kind_of Thin::Backends::Prefork, @server.backend
12
+ end
13
+
14
+ def test_pick_single_process_backend_if_no_workers
15
+ @server.worker_processes = 0
16
+ assert_kind_of Thin::Backends::SingleProcess, @server.backend
17
+ end
18
+
19
+ def test_cant_daemonize_single_process
20
+ @server.worker_processes = 0
21
+ assert_raise(NotImplementedError) do
22
+ silence_streams { @server.start(true) }
23
+ end
24
+ end
25
+
26
+ def test_do_not_preload_app
27
+ Thin::Server.new { fail "Should not load app before starting" }
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ require 'test_helper'
2
+
3
+ class SystemTest < Test::Unit::TestCase
4
+ def test_processor_count
5
+ assert_not_equal 0, Thin::System.processor_count
6
+ end
7
+
8
+ if Thin::System.java? || Thin::System.win?
9
+ def test_should_not_support_fork
10
+ assert ! Thin::System.supports_fork?
11
+ end
12
+ else
13
+ def test_should_support_fork
14
+ assert Thin::System.supports_fork?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "thin/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "thin"
7
+ s.version = Thin::VERSION::STRING
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Marc-Andre Cournoyer"]
10
+ s.email = ["thin@macournoyer.com"]
11
+ s.homepage = "http://code.macournoyer.com/thin"
12
+ s.summary = %q{A thin and fast web server}
13
+ s.description = %q{Thin is a Rack based, high performance web server}
14
+
15
+ s.rubyforge_project = "thin"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency "eventmachine-le", "~> 1.1.0"
23
+ s.add_dependency "http_parser.rb", "~> 0.5.3"
24
+ s.add_dependency "preforker", "~> 0.1.1"
25
+ s.add_dependency "rack", "~> 1.4"
26
+ end