friendlyfashion-thin 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/CHANGELOG +346 -0
  2. data/README.md +64 -0
  3. data/Rakefile +24 -0
  4. data/bin/thin +6 -0
  5. data/example/adapter.rb +32 -0
  6. data/example/async_app.ru +126 -0
  7. data/example/async_chat.ru +247 -0
  8. data/example/async_tailer.ru +100 -0
  9. data/example/config.ru +22 -0
  10. data/example/monit_sockets +20 -0
  11. data/example/monit_unixsock +20 -0
  12. data/example/myapp.rb +1 -0
  13. data/example/ramaze.ru +12 -0
  14. data/example/thin.god +80 -0
  15. data/example/thin_solaris_smf.erb +36 -0
  16. data/example/thin_solaris_smf.readme.txt +150 -0
  17. data/example/vlad.rake +64 -0
  18. data/ext/thin_parser/common.rl +55 -0
  19. data/ext/thin_parser/ext_help.h +14 -0
  20. data/ext/thin_parser/extconf.rb +6 -0
  21. data/ext/thin_parser/parser.c +1249 -0
  22. data/ext/thin_parser/parser.h +49 -0
  23. data/ext/thin_parser/parser.rl +157 -0
  24. data/ext/thin_parser/thin.c +436 -0
  25. data/lib/rack/adapter/loader.rb +75 -0
  26. data/lib/rack/adapter/rails.rb +183 -0
  27. data/lib/thin.rb +45 -0
  28. data/lib/thin/backends/base.rb +151 -0
  29. data/lib/thin/backends/swiftiply_client.rb +56 -0
  30. data/lib/thin/backends/tcp_server.rb +29 -0
  31. data/lib/thin/backends/unix_server.rb +56 -0
  32. data/lib/thin/command.rb +53 -0
  33. data/lib/thin/connection.rb +201 -0
  34. data/lib/thin/controllers/cluster.rb +178 -0
  35. data/lib/thin/controllers/controller.rb +188 -0
  36. data/lib/thin/controllers/service.rb +75 -0
  37. data/lib/thin/controllers/service.sh.erb +39 -0
  38. data/lib/thin/daemonizing.rb +185 -0
  39. data/lib/thin/headers.rb +39 -0
  40. data/lib/thin/logging.rb +54 -0
  41. data/lib/thin/request.rb +156 -0
  42. data/lib/thin/response.rb +104 -0
  43. data/lib/thin/runner.rb +222 -0
  44. data/lib/thin/server.rb +270 -0
  45. data/lib/thin/stats.html.erb +216 -0
  46. data/lib/thin/stats.rb +52 -0
  47. data/lib/thin/statuses.rb +43 -0
  48. data/lib/thin/version.rb +32 -0
  49. metadata +151 -0
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env rackup -s thin
2
+ #
3
+ # async_app.ru
4
+ # raggi/thin
5
+ #
6
+ # A second demo app for async rack + thin app processing!
7
+ # Now using http status code 100 instead.
8
+ #
9
+ # Created by James Tucker on 2008-06-17.
10
+ # Copyright 2008 James Tucker <raggi@rubyforge.org>.
11
+ #
12
+ #--
13
+ # Benchmark Results:
14
+ #
15
+ # raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/
16
+ # This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
17
+ # Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
18
+ # Copyright 2006 The Apache Software Foundation, http://www.apache.org/
19
+ #
20
+ # Benchmarking 127.0.0.1 (be patient)
21
+ # Completed 100 requests
22
+ # Completed 200 requests
23
+ # Completed 300 requests
24
+ # Completed 400 requests
25
+ # Finished 500 requests
26
+ #
27
+ #
28
+ # Server Software: thin
29
+ # Server Hostname: 127.0.0.1
30
+ # Server Port: 3000
31
+ #
32
+ # Document Path: /
33
+ # Document Length: 12 bytes
34
+ #
35
+ # Concurrency Level: 100
36
+ # Time taken for tests: 5.263089 seconds
37
+ # Complete requests: 500
38
+ # Failed requests: 0
39
+ # Write errors: 0
40
+ # Total transferred: 47000 bytes
41
+ # HTML transferred: 6000 bytes
42
+ # Requests per second: 95.00 [#/sec] (mean)
43
+ # Time per request: 1052.618 [ms] (mean)
44
+ # Time per request: 10.526 [ms] (mean, across all concurrent requests)
45
+ # Transfer rate: 8.55 [Kbytes/sec] received
46
+ #
47
+ # Connection Times (ms)
48
+ # min mean[+/-sd] median max
49
+ # Connect: 0 3 2.2 3 8
50
+ # Processing: 1042 1046 3.1 1046 1053
51
+ # Waiting: 1037 1042 3.6 1041 1050
52
+ # Total: 1045 1049 3.1 1049 1057
53
+ #
54
+ # Percentage of the requests served within a certain time (ms)
55
+ # 50% 1049
56
+ # 66% 1051
57
+ # 75% 1053
58
+ # 80% 1053
59
+ # 90% 1054
60
+ # 95% 1054
61
+ # 98% 1056
62
+ # 99% 1057
63
+ # 100% 1057 (longest request)
64
+
65
+ class DeferrableBody
66
+ include EventMachine::Deferrable
67
+
68
+ def call(body)
69
+ body.each do |chunk|
70
+ @body_callback.call(chunk)
71
+ end
72
+ end
73
+
74
+ def each &blk
75
+ @body_callback = blk
76
+ end
77
+
78
+ end
79
+
80
+ class AsyncApp
81
+
82
+ # This is a template async response. N.B. Can't use string for body on 1.9
83
+ AsyncResponse = [-1, {}, []].freeze
84
+
85
+ def call(env)
86
+
87
+ body = DeferrableBody.new
88
+
89
+ # Get the headers out there asap, let the client know we're alive...
90
+ EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
91
+
92
+ # Semi-emulate a long db request, instead of a timer, in reality we'd be
93
+ # waiting for the response data. Whilst this happens, other connections
94
+ # can be serviced.
95
+ # This could be any callback based thing though, a deferrable waiting on
96
+ # IO data, a db request, an http request, an smtp send, whatever.
97
+ EventMachine::add_timer(1) {
98
+ body.call ["Woah, async!\n"]
99
+
100
+ EventMachine::next_tick {
101
+ # This could actually happen any time, you could spawn off to new
102
+ # threads, pause as a good looking lady walks by, whatever.
103
+ # Just shows off how we can defer chunks of data in the body, you can
104
+ # even call this many times.
105
+ body.call ["Cheers then!"]
106
+ body.succeed
107
+ }
108
+ }
109
+
110
+ # throw :async # Still works for supporting non-async frameworks...
111
+
112
+ AsyncResponse # May end up in Rack :-)
113
+ end
114
+
115
+ end
116
+
117
+ # The additions to env for async.connection and async.callback absolutely
118
+ # destroy the speed of the request if Lint is doing it's checks on env.
119
+ # It is also important to note that an async response will not pass through
120
+ # any further middleware, as the async response notification has been passed
121
+ # right up to the webserver, and the callback goes directly there too.
122
+ # Middleware could possibly catch :async, and also provide a different
123
+ # async.connection and async.callback.
124
+
125
+ # use Rack::Lint
126
+ run AsyncApp.new
@@ -0,0 +1,247 @@
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
@@ -0,0 +1,100 @@
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