public
Description: A very fast & simple Ruby web server
Homepage: http://code.macournoyer.com/thin/
Clone URL: git://github.com/macournoyer/thin.git
Search Repo:
Merge branch 'swiftiply'
macournoyer (author)
Fri Feb 08 22:44:40 -0800 2008
commit  38c7717b080a74cdaec665258b6e874cd2e4482c
tree    01207199bcea7e39095ae97ba06732bfc5c331bb
parent  60d5c45f394923abde180f671ea02327b86576a2 parent  40ce6f3b68577026c83ff47afbeae8f19c8588fd
...
11
12
13
14
15
16
17
18
19
20
21
22
23
 
 
 
 
 
 
 
 
 
 
24
25
26
27
28
 
 
 
 
29
30
31
32
33
34
 
 
 
35
36
37
...
11
12
13
 
 
 
 
 
 
 
 
 
 
14
15
16
17
18
19
20
21
22
23
24
25
 
 
 
26
27
28
29
30
31
32
 
 
 
33
34
35
36
37
38
0
@@ -11,27 +11,28 @@
0
 require 'thin/statuses'
0
 
0
 module Thin
0
- autoload :Command, 'thin/command'
0
- autoload :Connection, 'thin/connection'
0
- autoload :Daemonizable, 'thin/daemonizing'
0
- autoload :Logging, 'thin/logging'
0
- autoload :Headers, 'thin/headers'
0
- autoload :Request, 'thin/request'
0
- autoload :Response, 'thin/response'
0
- autoload :Runner, 'thin/runner'
0
- autoload :Server, 'thin/server'
0
- autoload :Stats, 'thin/stats'
0
+ autoload :Command, 'thin/command'
0
+ autoload :Connection, 'thin/connection'
0
+ autoload :Daemonizable, 'thin/daemonizing'
0
+ autoload :Logging, 'thin/logging'
0
+ autoload :Headers, 'thin/headers'
0
+ autoload :Request, 'thin/request'
0
+ autoload :Response, 'thin/response'
0
+ autoload :Runner, 'thin/runner'
0
+ autoload :Server, 'thin/server'
0
+ autoload :Stats, 'thin/stats'
0
   
0
   module Connectors
0
- autoload :Connector, 'thin/connectors/connector'
0
- autoload :TcpServer, 'thin/connectors/tcp_server'
0
- autoload :UnixServer, 'thin/connectors/unix_server'
0
+ autoload :Connector, 'thin/connectors/connector'
0
+ autoload :SwiftiplyClient, 'thin/connectors/swiftiply_client'
0
+ autoload :TcpServer, 'thin/connectors/tcp_server'
0
+ autoload :UnixServer, 'thin/connectors/unix_server'
0
   end
0
   
0
   module Controllers
0
- autoload :Cluster, 'thin/controllers/cluster'
0
- autoload :Controller, 'thin/controllers/controller'
0
- autoload :Service, 'thin/controllers/service'
0
+ autoload :Cluster, 'thin/controllers/cluster'
0
+ autoload :Controller, 'thin/controllers/controller'
0
+ autoload :Service, 'thin/controllers/service'
0
   end
0
 end
0
 
...
30
31
32
33
34
 
 
 
35
36
37
...
30
31
32
 
 
33
34
35
36
37
38
0
@@ -30,8 +30,9 @@
0
     def shellify
0
       shellified_options = @options.inject([]) do |args, (name, value)|
0
         args << case value
0
- when NilClass
0
- when TrueClass then "--#{name}"
0
+ when NilClass,
0
+ TrueClass then "--#{name}"
0
+ when FalseClass
0
         else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
0
         end
0
       end
...
38
39
40
41
 
42
43
44
45
...
47
48
49
 
 
50
51
52
53
 
 
 
 
54
55
56
...
38
39
40
 
41
42
43
44
45
...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
0
@@ -38,7 +38,7 @@
0
         send_data chunk
0
       end
0
       
0
- close_connection_after_writing
0
+ close_connection_after_writing unless persistent?
0
       
0
     rescue Object => e
0
       log "Unexpected error while processing request: #{e.message}"
0
0
@@ -47,10 +47,16 @@
0
     ensure
0
       @request.close rescue nil
0
       @response.close rescue nil
0
+
0
+ post_init if persistent?
0
     end
0
     
0
     def unbind
0
       @connector.connection_finished(self)
0
+ end
0
+
0
+ def persistent?
0
+ false
0
     end
0
     
0
     protected
...
22
23
24
 
 
 
 
25
26
27
...
22
23
24
25
26
27
28
29
30
31
0
@@ -22,6 +22,10 @@
0
       def close
0
       end
0
       
0
+ def running?
0
+ @server.running?
0
+ end
0
+
0
       def server=(server)
0
         @server = server
0
         @silent = @server.silent
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
0
@@ -1 +1,56 @@
0
+module Thin
0
+ module Connectors
0
+ class SwiftiplyClient < Connector
0
+ attr_accessor :key
0
+
0
+ attr_accessor :host, :port
0
+
0
+ def initialize(host, port, key=nil)
0
+ @host = host
0
+ @port = port.to_i
0
+ @key = key || ''
0
+ super()
0
+ end
0
+
0
+ # Connect the server
0
+ def connect
0
+ EventMachine.connect(@host, @port, SwiftiplyConnection, &method(:initialize_connection))
0
+ end
0
+
0
+ # Stops the server
0
+ def disconnect
0
+ EventMachine.stop
0
+ end
0
+
0
+ def to_s
0
+ "#{@host}:#{@port} swiftiply"
0
+ end
0
+ end
0
+ end
0
+
0
+ class SwiftiplyConnection < Connection
0
+ def connection_completed
0
+ send_data swiftiply_handshake(@connector.key)
0
+ end
0
+
0
+ def persistent?
0
+ true
0
+ end
0
+
0
+ def unbind
0
+ super
0
+ EventMachine.add_timer(rand(2)) { reconnect(@connector.host, @connector.port) } if @connector.running?
0
+ end
0
+
0
+ protected
0
+ def swiftiply_handshake(key)
0
+ 'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @connector.port) << sprintf('%02x', key.length) << key
0
+ end
0
+
0
+ # For some reason Swiftiply request the current host
0
+ def host_ip
0
+ Socket.gethostbyname(@connector.host)[3].unpack('CCCC') rescue [0,0,0,0]
0
+ end
0
+ end
0
+end
...
25
26
27
 
 
 
 
28
29
30
31
 
32
33
34
...
60
61
62
 
 
63
64
65
...
80
81
82
83
 
84
85
86
...
88
89
90
 
 
91
92
93
94
...
97
98
99
 
 
100
101
102
103
 
104
105
106
...
25
26
27
28
29
30
31
32
33
34
 
35
36
37
38
...
64
65
66
67
68
69
70
71
...
86
87
88
 
89
90
91
92
...
94
95
96
97
98
99
100
101
102
...
105
106
107
108
109
110
 
 
 
111
112
113
114
0
@@ -25,10 +25,14 @@
0
       def socket; @options[:socket] end
0
       def pid_file; @options[:pid] end
0
       def log_file; @options[:log] end
0
+
0
+ def swiftiply?
0
+ @options.has_key?(:swiftiply)
0
+ end
0
     
0
       # Start the servers
0
       def start
0
- with_each_server { |port| start_server port }
0
+ with_each_server { |n| start_server n }
0
       end
0
     
0
       # Start a single server
0
@@ -60,6 +64,8 @@
0
       def server_id(number)
0
         if socket
0
           socket_for(number)
0
+ elsif swiftiply?
0
+ [address, first_port, number].join(':')
0
         else
0
           [address, number].join(':')
0
         end
0
@@ -80,7 +86,7 @@
0
       def pid_for(number)
0
         File.read(pid_file_for(number)).chomp.to_i
0
       end
0
-
0
+
0
       private
0
         # Send the command to the +thin+ script
0
         def run(cmd, options, number)
0
@@ -88,6 +94,8 @@
0
           cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number))
0
           if socket
0
             cmd_options.merge!(:socket => socket_for(number))
0
+ elsif swiftiply?
0
+ cmd_options.merge!(:port => first_port)
0
           else
0
             cmd_options.merge!(:port => number)
0
           end
0
0
@@ -97,10 +105,10 @@
0
         def with_each_server
0
           if @only
0
             yield @only
0
+ elsif socket || swiftiply?
0
+ @size.times { |n| yield n }
0
           else
0
- @size.times do |n|
0
- yield socket ? n : (first_port + n)
0
- end
0
+ @size.times { |n| yield first_port + n }
0
           end
0
         end
0
       
...
22
23
24
25
26
 
 
 
 
 
27
28
 
29
30
31
...
22
23
24
 
 
25
26
27
28
29
30
 
31
32
33
34
0
@@ -22,10 +22,13 @@
0
       end
0
     
0
       def start
0
- if @options[:socket]
0
- server = Server.new(@options[:socket])
0
+ server = case
0
+ when @options.has_key?(:socket)
0
+ Server.new(@options[:socket])
0
+ when @options.has_key?(:swiftiply)
0
+ Server.new(Connectors::SwiftiplyClient.new(@options[:address], @options[:port], @options[:swiftiply]))
0
         else
0
- server = Server.new(@options[:address], @options[:port])
0
+ Server.new(@options[:address], @options[:port])
0
         end
0
 
0
         server.pid_file = @options[:pid]
...
59
60
61
 
62
63
64
...
59
60
61
62
63
64
65
0
@@ -59,6 +59,7 @@
0
                                         "(default: #{@options[:address]})") { |host| @options[:address] = host }
0
         opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i }
0
         opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file }
0
+ opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key }
0
         opts.on("-e", "--environment ENV", "Rails environment " +
0
                                            "(default: #{@options[:environment]})") { |env| @options[:environment] = env }
0
         opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) }
...
2
3
4
5
 
6
7
8
9
10
11
12
 
13
14
...
2
3
4
 
5
6
7
8
9
10
11
 
12
13
14
0
@@ -2,14 +2,14 @@
0
 
0
 describe Command do
0
   before do
0
- @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log', :pid => nil)
0
+ @command = Command.new(:start, :port => 3000, :daemonize => true, :log => 'hi.log')
0
     @command.silent = true
0
   end
0
   
0
   it 'should shellify command' do
0
     out = @command.shellify
0
     out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
0
- out.should_not include('--pid=')
0
+ out.should_not include('--pid')
0
   end
0
 end
...
146
147
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
0
@@ -146,4 +146,48 @@
0
       { :daemonize => true, :log => "thin.#{port}.log", :timeout => 10, :address => "0.0.0.0", :port => port, :pid => "thin.#{port}.pid", :chdir => "/rails_app" }
0
     end
0
 end
0
+
0
+describe Cluster, "with Swiftiply" do
0
+ before do
0
+ @cluster = Cluster.new(:chdir => '/rails_app',
0
+ :address => '0.0.0.0',
0
+ :port => 3000,
0
+ :servers => 3,
0
+ :timeout => 10,
0
+ :log => 'thin.log',
0
+ :pid => 'thin.pid',
0
+ :swiftiply => true
0
+ )
0
+ @cluster.silent = true
0
+ end
0
+
0
+ it 'should call each server' do
0
+ calls = []
0
+ @cluster.send(:with_each_server) do |n|
0
+ calls << n
0
+ end
0
+ calls.should == [0, 1, 2]
0
+ end
0
+
0
+ it 'should start each server' do
0
+ Command.should_receive(:run).with(:start, options_for_swiftiply(0))
0
+ Command.should_receive(:run).with(:start, options_for_swiftiply(1))
0
+ Command.should_receive(:run).with(:start, options_for_swiftiply(2))
0
+
0
+ @cluster.start
0
+ end
0
+
0
+ it 'should stop each server' do
0
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(0))
0
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(1))
0
+ Command.should_receive(:run).with(:stop, options_for_swiftiply(2))
0
+
0
+ @cluster.stop
0
+ end
0
+
0
+ private
0
+ def options_for_swiftiply(number)
0
+ { :address => '0.0.0.0', :port => 3000, :daemonize => true, :log => "thin.#{number}.log", :timeout => 10, :pid => "thin.#{number}.pid", :chdir => "/rails_app", :swiftiply => true }
0
+ end
0
+end
...
 
 
 
 
 
 
...
1
2
3
4
5
6
0
@@ -1 +1,7 @@
0
+cluster_address: 0.0.0.0
0
+cluster_port: 3333
0
+map:
0
+ - incoming: 127.0.0.1
0
+ outgoing: 127.0.0.1:5555
0
+ default: true
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0
@@ -1 +1,28 @@
0
+require File.dirname(__FILE__) + '/../spec_helper'
0
+
0
+describe Server, 'on Swiftiply' do
0
+ before do
0
+ @swiftiply = fork do
0
+ exec "swiftiply -c #{File.dirname(__FILE__)}/swiftiply.yml"
0
+ end
0
+ sleep 0.5
0
+ start_server(Connectors::SwiftiplyClient.new('0.0.0.0', 5555, nil)) do |env|
0
+ body = env.inspect + env['rack.input'].read
0
+ [200, { 'Content-Type' => 'text/html', 'Content-Length' => body.size.to_s }, body]
0
+ end
0
+ end
0
+
0
+ it 'should GET from Net::HTTP' do
0
+ Net::HTTP.get(URI.parse("http://0.0.0.0:3333/?cthis")).should include('cthis')
0
+ end
0
+
0
+ it 'should POST from Net::HTTP' do
0
+ Net::HTTP.post_form(URI.parse("http://0.0.0.0:3333/"), :arg => 'pirate').body.should include('arg=pirate')
0
+ end
0
+
0
+ after do
0
+ stop_server
0
+ Process.kill(9, @swiftiply)
0
+ end
0
+end

Comments

    No one has commented yet.