thin 1.8.0 → 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 (108) 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 -76
  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 +11 -26
  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 +138 -93
  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/rack/handler/thin.rb +0 -38
  94. data/lib/thin/backends/base.rb +0 -169
  95. data/lib/thin/backends/swiftiply_client.rb +0 -56
  96. data/lib/thin/backends/tcp_server.rb +0 -34
  97. data/lib/thin/backends/unix_server.rb +0 -56
  98. data/lib/thin/command.rb +0 -53
  99. data/lib/thin/controllers/cluster.rb +0 -178
  100. data/lib/thin/controllers/controller.rb +0 -189
  101. data/lib/thin/controllers/service.rb +0 -76
  102. data/lib/thin/controllers/service.sh.erb +0 -39
  103. data/lib/thin/daemonizing.rb +0 -199
  104. data/lib/thin/headers.rb +0 -40
  105. data/lib/thin/logging.rb +0 -174
  106. data/lib/thin/stats.html.erb +0 -216
  107. data/lib/thin/stats.rb +0 -52
  108. data/lib/thin/statuses.rb +0 -48
@@ -1,247 +0,0 @@
1
- #!/usr/bin/env rackup -s thin
2
- #
3
- # async_chat.ru
4
- # raggi/thin
5
- #
6
- # Created by James Tucker on 2008-06-19.
7
- # Copyright 2008 James Tucker <raggi@rubyforge.org>.
8
-
9
- # Uncomment if appropriate for you..
10
- EM.epoll
11
- # EM.kqueue # bug on OS X in 0.12?
12
-
13
- class DeferrableBody
14
- include EventMachine::Deferrable
15
-
16
- def initialize
17
- @queue = []
18
- end
19
-
20
- def schedule_dequeue
21
- return unless @body_callback
22
- EventMachine::next_tick do
23
- next unless body = @queue.shift
24
- body.each do |chunk|
25
- @body_callback.call(chunk)
26
- end
27
- schedule_dequeue unless @queue.empty?
28
- end
29
- end
30
-
31
- def call(body)
32
- @queue << body
33
- schedule_dequeue
34
- end
35
-
36
- def each &blk
37
- @body_callback = blk
38
- schedule_dequeue
39
- end
40
-
41
- end
42
-
43
- class Chat
44
-
45
- module UserBody
46
- attr_accessor :username
47
- end
48
-
49
- def initialize
50
- @users = {}
51
- end
52
-
53
- def render_page
54
- [] << <<-EOPAGE
55
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
56
- <html>
57
- <head>
58
- <style>
59
- body {
60
- font-family: sans-serif;
61
- margin: 0;
62
- padding: 0;
63
- margin-top: 4em;
64
- margin-bottom: 1em;
65
- }
66
- #header {
67
- background: silver;
68
- height: 4em;
69
- width: 100%;
70
- position: fixed;
71
- top: 0px;
72
- border-bottom: 1px solid black;
73
- padding-left: 0.5em;
74
- }
75
- #messages {
76
- width: 100%;
77
- height: 100%;
78
- }
79
- .message {
80
- margin-left: 1em;
81
- }
82
- #send_form {
83
- position: fixed;
84
- bottom: 0px;
85
- height: 1em;
86
- width: 100%;
87
- }
88
- #message_box {
89
- background: silver;
90
- width: 100%;
91
- border: 0px;
92
- border-top: 1px solid black;
93
- }
94
- .gray {
95
- color: gray;
96
- }
97
- </style>
98
- <script type="text/javascript" src="http://ra66i.org/tmp/jquery-1.2.6.min.js"></script>
99
- <script type="text/javascript">
100
- XHR = function() {
101
- var request = false;
102
- try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {
103
- try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch(e1) {
104
- try { request = new XMLHttpRequest(); } catch(e2) {
105
- return false;
106
- }
107
- }
108
- }
109
- return request;
110
- }
111
- scroll = function() {
112
- window.scrollBy(0,50);
113
- setTimeout('scroll()',100);
114
- }
115
- focus = function() {
116
- $('#message_box').focus();
117
- }
118
- send_message = function(message_box) {
119
- xhr = XHR();
120
- xhr.open("POST", "/", true);
121
- xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
122
- xhr.setRequestHeader("X_REQUESTED_WITH", "XMLHttpRequest");
123
- xhr.send("message="+escape(message_box.value));
124
- scroll();
125
- message_box.value = '';
126
- focus();
127
- return false;
128
- }
129
- new_message = function(username, message) {
130
- // TODO html escape message
131
- formatted_message = "<div class='message'>" + username + ": " + message + "</div>";
132
- messages_div = $('#messages');
133
- $(formatted_message).appendTo(messages_div);
134
- scroll();
135
- return true;
136
- }
137
- </script>
138
- <title>Async Chat</title>
139
- </head>
140
- <body>
141
- <div id="header">
142
- <h1>Async Chat</h1>
143
- </div>
144
- <div id="messages" onclick="focus();">
145
- <span class="gray">Your first message will become your nickname!</span>
146
- <span>Users: #{@users.map{|k,u|u.username}.join(', ')}</span>
147
- </div>
148
- <form id="send_form" onSubmit="return send_message(this.message)">
149
- <input type="text" id="message_box" name="message"></input>
150
- </form>
151
- <script type="text/javascript">focus();</script>
152
- </body>
153
- </html>
154
- EOPAGE
155
- end
156
-
157
- def register_user(user_id, renderer)
158
- body = create_user(user_id)
159
- body.call render_page
160
- body.errback { delete_user user_id }
161
- body.callback { delete_user user_id }
162
-
163
- EventMachine::next_tick do
164
- renderer.call [200, {'Content-Type' => 'text/html'}, body]
165
- end
166
- end
167
-
168
- def new_message(user_id, message)
169
- return unless @users[user_id]
170
- if @users[user_id].username == :anonymous
171
- username = unique_username(message)
172
- log "User: #{user_id} is #{username}"
173
- @users[user_id].username = message
174
- message = "<span class='gray'>-> #{username} signed on.</span>"
175
- end
176
- username ||= @users[user_id].username
177
- log "User: #{username} sent: #{message}"
178
- @users.each do |id, body|
179
- EventMachine::next_tick { body.call [js_message(username, message)] }
180
- end
181
- end
182
-
183
- private
184
- def unique_username(name)
185
- name.concat('_') while @users.any? { |id,u| name == u.username }
186
- name
187
- end
188
-
189
- def log(str)
190
- print str, "\n"
191
- end
192
-
193
- def add_user(id, body)
194
- @users[id] = body
195
- end
196
-
197
- def delete_user(id)
198
- message = "User: #{id} - #{@users[id].username if @users[id]} disconnected."
199
- log message
200
- new_message(id, message)
201
- @users.delete id
202
- end
203
-
204
- def js_message(username, message)
205
- %(<script type="text/javascript">new_message("#{username}","#{message}");</script>)
206
- end
207
-
208
- def create_user(id)
209
- message = "User: #{id} connected."
210
- log message
211
- new_message(id, message)
212
- body = DeferrableBody.new
213
- body.extend UserBody
214
- body.username = :anonymous
215
- add_user(id, body)
216
- body
217
- end
218
-
219
- end
220
-
221
- class AsyncChat
222
-
223
- AsyncResponse = [-1, {}, []].freeze
224
- AjaxResponse = [200, {}, []].freeze
225
-
226
- def initialize
227
- @chat = Chat.new
228
- end
229
-
230
- def call(env)
231
- request = Rack::Request.new(env)
232
- # TODO - cookie me, baby
233
- user_id = request.env['REMOTE_ADDR']
234
- if request.xhr?
235
- message = request['message']
236
- @chat.new_message(user_id, Rack::Utils.escape_html(message))
237
- AjaxResponse
238
- else
239
- renderer = request.env['async.callback']
240
- @chat.register_user(user_id, renderer)
241
- AsyncResponse
242
- end
243
- end
244
-
245
- end
246
-
247
- run AsyncChat.new
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env rackup -s thin
2
- #
3
- # async_tailer.ru
4
- # raggi/thin
5
- #
6
- # Tested with 150 spawned tails on OS X
7
- #
8
- # Created by James Tucker on 2008-06-18.
9
- # Copyright 2008 James Tucker <raggi@rubyforge.org>.
10
-
11
- # Uncomment if appropriate for you..
12
- # EM.epoll
13
- # EM.kqueue
14
-
15
- class DeferrableBody
16
- include EventMachine::Deferrable
17
-
18
- def initialize
19
- @queue = []
20
- # make sure to flush out the queue before closing the connection
21
- callback{
22
- until @queue.empty?
23
- @queue.shift.each{|chunk| @body_callback.call(chunk) }
24
- end
25
- }
26
- end
27
-
28
- def schedule_dequeue
29
- return unless @body_callback
30
- EventMachine::next_tick do
31
- next unless body = @queue.shift
32
- body.each do |chunk|
33
- @body_callback.call(chunk)
34
- end
35
- schedule_dequeue unless @queue.empty?
36
- end
37
- end
38
-
39
- def call(body)
40
- @queue << body
41
- schedule_dequeue
42
- end
43
-
44
- def each &blk
45
- @body_callback = blk
46
- schedule_dequeue
47
- end
48
-
49
- end
50
-
51
- module TailRenderer
52
- attr_accessor :callback
53
-
54
- def receive_data(data)
55
- @callback.call([data])
56
- end
57
-
58
- def unbind
59
- @callback.succeed
60
- end
61
- end
62
-
63
- class AsyncTailer
64
-
65
- AsyncResponse = [-1, {}, []].freeze
66
-
67
- def call(env)
68
-
69
- body = DeferrableBody.new
70
-
71
- EventMachine::next_tick do
72
-
73
- env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body]
74
-
75
- body.call ["<h1>Async Tailer</h1><pre>"]
76
-
77
- end
78
-
79
- EventMachine::popen('tail -f /var/log/system.log', TailRenderer) do |t|
80
-
81
- t.callback = body
82
-
83
- # If for some reason we 'complete' body, close the tail.
84
- body.callback do
85
- t.close_connection
86
- end
87
-
88
- # If for some reason the client disconnects, close the tail.
89
- body.errback do
90
- t.close_connection
91
- end
92
-
93
- end
94
-
95
- AsyncResponse
96
- end
97
-
98
- end
99
-
100
- run AsyncTailer.new
@@ -1,22 +0,0 @@
1
- # Run with: rackup -s thin
2
- # then browse to http://localhost:9292
3
- # Or with: thin start -R config.ru
4
- # then browse to http://localhost:3000
5
- #
6
- # Check Rack::Builder doc for more details on this file format:
7
- # http://rack.rubyforge.org/doc/classes/Rack/Builder.html
8
- require 'thin'
9
-
10
- app = proc do |env|
11
- # Response body has to respond to each and yield strings
12
- # See Rack specs for more info: http://rack.rubyforge.org/doc/files/SPEC.html
13
- body = ['hi!']
14
-
15
- [
16
- 200, # Status code
17
- { 'Content-Type' => 'text/html' }, # Reponse headers
18
- body # Body of the response
19
- ]
20
- end
21
-
22
- run app
@@ -1,20 +0,0 @@
1
- check process blog1
2
- with pidfile /u/apps/blog/shared/pids/thin.14000.pid
3
- start program = "ruby thin start -d -e production -u nobody -g nobody -p 14000 -a 127.0.0.1 -P tmp/pids/thin.14000.pid -c /u/apps/blog/current"
4
- stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14000.pid"
5
- if totalmem > 90.0 MB for 5 cycles then restart
6
- if failed port 14000 then restart
7
- if cpu usage > 95% for 3 cycles then restart
8
- if 5 restarts within 5 cycles then timeout
9
- group blog
10
-
11
- check process blog2
12
- with pidfile /u/apps/blog/shared/pids/thin.14001.pid
13
- start program = "ruby thin start -d -e production -u nobody -g nobody -p 14001 -a 127.0.0.1 -P tmp/pids/thin.14001.pid -c /u/apps/blog/current"
14
- stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14001.pid"
15
- if totalmem > 90.0 MB for 5 cycles then restart
16
- if failed port 14001 then restart
17
- if cpu usage > 95% for 3 cycles then restart
18
- if 5 restarts within 5 cycles then timeout
19
- group blog
20
-
@@ -1,20 +0,0 @@
1
- check process blog1
2
- with pidfile /u/apps/blog/shared/pids/thin.1.pid
3
- start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.1.sock -P tmp/pids/thin.1.pid -c /u/apps/blog/current"
4
- stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.1.pid"
5
- if totalmem > 90.0 MB for 5 cycles then restart
6
- if failed unixsocket /u/apps/blog/shared/pids/thin.1.sock then restart
7
- if cpu usage > 95% for 3 cycles then restart
8
- if 5 restarts within 5 cycles then timeout
9
- group blog
10
-
11
- check process blog2
12
- with pidfile /u/apps/blog/shared/pids/thin.2.pid
13
- start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.2.sock -P tmp/pids/thin.2.pid -c /u/apps/blog/current"
14
- stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.2.pid"
15
- if totalmem > 90.0 MB for 5 cycles then restart
16
- if failed unixsocket /u/apps/blog/shared/pids/thin.2.sock then restart
17
- if cpu usage > 95% for 3 cycles then restart
18
- if 5 restarts within 5 cycles then timeout
19
- group blog
20
-
@@ -1 +0,0 @@
1
- Myapp = lambda { |env| [200, {}, 'this is my app!'] }
@@ -1,12 +0,0 @@
1
- # Ramaze Rackup config file.
2
- # by tmm1
3
- # Use with --rackup option:
4
- #
5
- # thin start -r ramaze.ru
6
- #
7
- require 'start'
8
-
9
- Ramaze.trait[:essentials].delete Ramaze::Adapter
10
- Ramaze.start :force => true
11
-
12
- run Ramaze::Adapter::Base
@@ -1,80 +0,0 @@
1
- # == God config file
2
- # http://god.rubyforge.org/
3
- # Authors: Gump and michael@glauche.de
4
- #
5
- # Config file for god that configures watches for each instance of a thin server for
6
- # each thin configuration file found in /etc/thin.
7
- # In order to get it working on Ubuntu, I had to make a change to god as noted at
8
- # the following blog:
9
- # http://blog.alexgirard.com/ruby-one-line-to-save-god/
10
- #
11
- require 'yaml'
12
-
13
- config_path = "/etc/thin"
14
-
15
- Dir[config_path + "/*.yml"].each do |file|
16
- config = YAML.load_file(file)
17
- num_servers = config["servers"] ||= 1
18
-
19
- (0...num_servers).each do |i|
20
- # UNIX socket cluster use number 0 to 2 (for 3 servers)
21
- # and tcp cluster use port number 3000 to 3002.
22
- number = config['socket'] ? i : (config['port'] + i)
23
-
24
- God.watch do |w|
25
- w.group = "thin-" + File.basename(file, ".yml")
26
- w.name = w.group + "-#{number}"
27
-
28
- w.interval = 30.seconds
29
-
30
- w.uid = config["user"]
31
- w.gid = config["group"]
32
-
33
- w.start = "thin start -C #{file} -o #{number}"
34
- w.start_grace = 10.seconds
35
-
36
- w.stop = "thin stop -C #{file} -o #{number}"
37
- w.stop_grace = 10.seconds
38
-
39
- w.restart = "thin restart -C #{file} -o #{number}"
40
-
41
- pid_path = config["chdir"] + "/" + config["pid"]
42
- ext = File.extname(pid_path)
43
-
44
- w.pid_file = pid_path.gsub(/#{ext}$/, ".#{number}#{ext}")
45
-
46
- w.behavior(:clean_pid_file)
47
-
48
- w.start_if do |start|
49
- start.condition(:process_running) do |c|
50
- c.interval = 5.seconds
51
- c.running = false
52
- end
53
- end
54
-
55
- w.restart_if do |restart|
56
- restart.condition(:memory_usage) do |c|
57
- c.above = 150.megabytes
58
- c.times = [3,5] # 3 out of 5 intervals
59
- end
60
-
61
- restart.condition(:cpu_usage) do |c|
62
- c.above = 50.percent
63
- c.times = 5
64
- end
65
- end
66
-
67
- w.lifecycle do |on|
68
- on.condition(:flapping) do |c|
69
- c.to_state = [:start, :restart]
70
- c.times = 5
71
- c.within = 5.minutes
72
- c.transition = :unmonitored
73
- c.retry_in = 10.minutes
74
- c.retry_times = 5
75
- c.retry_within = 2.hours
76
- end
77
- end
78
- end
79
- end
80
- end