thin 0.5.0 → 0.5.2
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.
- data/COPYING +1 -1
- data/README +3 -7
- data/Rakefile +6 -163
- data/bin/thin +87 -48
- data/example/thin.god +72 -0
- data/ext/thin_parser/thin.c +7 -7
- data/lib/rack/adapter/rails.rb +38 -22
- data/lib/thin.rb +2 -1
- data/lib/thin/cluster.rb +106 -0
- data/lib/thin/connection.rb +3 -4
- data/lib/thin/daemonizing.rb +4 -24
- data/lib/thin/request.rb +6 -5
- data/lib/thin/response.rb +1 -3
- data/lib/thin/server.rb +9 -6
- data/lib/thin/version.rb +6 -4
- data/lib/thin_parser.bundle +0 -0
- data/spec/cluster_spec.rb +58 -0
- data/spec/daemonizing_spec.rb +1 -2
- data/spec/rack_rails_spec.rb +73 -0
- data/spec/rails_app/app/controllers/application.rb +10 -0
- data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
- data/spec/rails_app/app/helpers/application_helper.rb +3 -0
- data/spec/rails_app/app/views/simple/index.html.erb +15 -0
- data/spec/rails_app/config/boot.rb +109 -0
- data/spec/rails_app/config/environment.rb +64 -0
- data/spec/rails_app/config/environments/development.rb +18 -0
- data/spec/rails_app/config/environments/production.rb +19 -0
- data/spec/rails_app/config/environments/test.rb +22 -0
- data/spec/rails_app/config/initializers/inflections.rb +10 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/routes.rb +35 -0
- data/spec/rails_app/public/404.html +30 -0
- data/spec/rails_app/public/422.html +30 -0
- data/spec/rails_app/public/500.html +30 -0
- data/spec/rails_app/public/dispatch.cgi +10 -0
- data/spec/rails_app/public/dispatch.fcgi +24 -0
- data/spec/rails_app/public/dispatch.rb +10 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/images/rails.png +0 -0
- data/spec/rails_app/public/index.html +277 -0
- data/spec/rails_app/public/javascripts/application.js +2 -0
- data/spec/rails_app/public/javascripts/controls.js +963 -0
- data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
- data/spec/rails_app/public/javascripts/effects.js +1120 -0
- data/spec/rails_app/public/javascripts/prototype.js +4225 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/about +3 -0
- data/spec/rails_app/script/console +3 -0
- data/spec/rails_app/script/destroy +3 -0
- data/spec/rails_app/script/generate +3 -0
- data/spec/rails_app/script/performance/benchmarker +3 -0
- data/spec/rails_app/script/performance/profiler +3 -0
- data/spec/rails_app/script/performance/request +3 -0
- data/spec/rails_app/script/plugin +3 -0
- data/spec/rails_app/script/process/inspector +3 -0
- data/spec/rails_app/script/process/reaper +3 -0
- data/spec/rails_app/script/process/spawner +3 -0
- data/spec/rails_app/script/runner +3 -0
- data/spec/rails_app/script/server +3 -0
- data/spec/response_spec.rb +16 -0
- data/spec/spec_helper.rb +1 -0
- metadata +71 -11
- data/doc/rdoc/created.rid +0 -1
- data/doc/rdoc/files/README.html +0 -197
- data/doc/rdoc/index.html +0 -10
- data/doc/rdoc/logo.gif +0 -0
- data/doc/rdoc/rdoc-style.css +0 -91
data/ext/thin_parser/thin.c
CHANGED
@@ -77,7 +77,7 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
|
|
77
77
|
f = rb_str_dup(global_http_prefix);
|
78
78
|
f = rb_str_buf_cat(f, field, flen);
|
79
79
|
|
80
|
-
for(ch =
|
80
|
+
for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) {
|
81
81
|
if(*ch == '-') {
|
82
82
|
*ch = '_';
|
83
83
|
} else {
|
@@ -176,12 +176,12 @@ void header_done(void *data, const char *at, size_t length)
|
|
176
176
|
rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
|
177
177
|
if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
|
178
178
|
/* ruby better close strings off with a '\0' dammit */
|
179
|
-
colon = strchr(
|
179
|
+
colon = strchr(RSTRING_PTR(temp), ':');
|
180
180
|
if(colon != NULL) {
|
181
|
-
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon -
|
181
|
+
rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
|
182
182
|
rb_hash_aset(req, global_server_port,
|
183
|
-
rb_str_substr(temp, colon -
|
184
|
-
|
183
|
+
rb_str_substr(temp, colon - RSTRING_PTR(temp)+1,
|
184
|
+
RSTRING_LEN(temp)));
|
185
185
|
} else {
|
186
186
|
rb_hash_aset(req, global_server_name, temp);
|
187
187
|
rb_hash_aset(req, global_server_port, global_port_80);
|
@@ -313,8 +313,8 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
313
313
|
DATA_GET(self, http_parser, http);
|
314
314
|
|
315
315
|
from = FIX2INT(start);
|
316
|
-
dptr =
|
317
|
-
dlen =
|
316
|
+
dptr = RSTRING_PTR(data);
|
317
|
+
dlen = RSTRING_LEN(data);
|
318
318
|
|
319
319
|
if(from >= dlen) {
|
320
320
|
rb_raise(eHttpParserError, "Requested start is after data buffer end.");
|
data/lib/rack/adapter/rails.rb
CHANGED
@@ -14,8 +14,8 @@ module Rack
|
|
14
14
|
module Adapter
|
15
15
|
class Rails
|
16
16
|
def initialize(options={})
|
17
|
-
@root = options[:root]
|
18
|
-
@env = options[:
|
17
|
+
@root = options[:root] || Dir.pwd
|
18
|
+
@env = options[:environment] || 'development'
|
19
19
|
|
20
20
|
load_application
|
21
21
|
|
@@ -30,15 +30,16 @@ module Rack
|
|
30
30
|
end
|
31
31
|
|
32
32
|
# TODO refactor this in File#can_serve?(path) ??
|
33
|
-
def
|
33
|
+
def file_exist?(path)
|
34
34
|
full_path = ::File.join(@file_server.root, Utils.unescape(path))
|
35
35
|
::File.file?(full_path) && ::File.readable?(full_path)
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
def serve_file(env)
|
39
|
+
@file_server.call(env)
|
40
|
+
end
|
41
|
+
|
42
|
+
def serve_rails(env)
|
42
43
|
request = Request.new(env)
|
43
44
|
response = Response.new
|
44
45
|
|
@@ -49,6 +50,20 @@ module Rack
|
|
49
50
|
|
50
51
|
response.finish
|
51
52
|
end
|
53
|
+
|
54
|
+
def call(env)
|
55
|
+
path = env['PATH_INFO'].chomp('/')
|
56
|
+
cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
|
57
|
+
|
58
|
+
if file_exist?(path) # Serve the file if it's there
|
59
|
+
serve_file(env)
|
60
|
+
elsif file_exist?(cached_path) # Serve the page cache if it's there
|
61
|
+
env['PATH_INFO'] = cached_path
|
62
|
+
serve_file(env)
|
63
|
+
else # No static file, let Rails handle it
|
64
|
+
serve_rails(env)
|
65
|
+
end
|
66
|
+
end
|
52
67
|
|
53
68
|
protected
|
54
69
|
|
@@ -75,27 +90,28 @@ module Rack
|
|
75
90
|
@response['Expires'] = options.delete('expires') if options['expires']
|
76
91
|
|
77
92
|
@response.status = options.delete('Status') if options['Status']
|
78
|
-
|
79
|
-
options.each { |k,v| @response[k] = v }
|
80
|
-
|
93
|
+
|
81
94
|
# Convert 'cookie' header to 'Set-Cookie' headers.
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
when
|
90
|
-
|
95
|
+
# Because Set-Cookie header can appear more the once in the response body,
|
96
|
+
# we store it in a line break seperated string that will be translated to
|
97
|
+
# multiple Set-Cookie header by the handler.
|
98
|
+
if cookie = options.delete('cookie')
|
99
|
+
cookies = []
|
100
|
+
|
101
|
+
case cookie
|
102
|
+
when Array then cookie.each { |c| cookies << c.to_s }
|
103
|
+
when Hash then cookie.each { |_, c| cookies << c.to_s }
|
104
|
+
else cookies << cookie.to_s
|
91
105
|
end
|
92
106
|
|
93
|
-
|
107
|
+
@output_cookies.each { |c| cookies << c.to_s } if @output_cookies
|
94
108
|
|
95
|
-
@response['Set-Cookie'] = cookies
|
109
|
+
@response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
|
96
110
|
end
|
111
|
+
|
112
|
+
options.each { |k,v| @response[k] = v }
|
97
113
|
end
|
98
|
-
|
114
|
+
|
99
115
|
""
|
100
116
|
end
|
101
117
|
|
data/lib/thin.rb
CHANGED
@@ -12,8 +12,9 @@ require 'thin/statuses'
|
|
12
12
|
|
13
13
|
module Thin
|
14
14
|
NAME = 'thin'.freeze
|
15
|
-
SERVER = "#{NAME} #{VERSION::STRING}".freeze
|
15
|
+
SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
|
16
16
|
|
17
|
+
autoload :Cluster, 'thin/cluster'
|
17
18
|
autoload :Connection, 'thin/connection'
|
18
19
|
autoload :Daemonizable, 'thin/daemonizing'
|
19
20
|
autoload :Logging, 'thin/logging'
|
data/lib/thin/cluster.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module Thin
|
2
|
+
# Control a set of servers.
|
3
|
+
# * Generate start and stop commands and run them.
|
4
|
+
# * Inject the port number in the pid and log filenames.
|
5
|
+
# Servers are started throught the +thin+ commandline script.
|
6
|
+
class Cluster
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
# Path to the +thin+ script used to control the servers.
|
10
|
+
# Leave this to default to use the one in the path.
|
11
|
+
attr_accessor :script
|
12
|
+
|
13
|
+
# Number of servers in the cluster.
|
14
|
+
attr_accessor :size
|
15
|
+
|
16
|
+
# Command line options passed to the thin script
|
17
|
+
attr_accessor :options
|
18
|
+
|
19
|
+
# Create a new cluster of servers launched using +options+.
|
20
|
+
def initialize(options)
|
21
|
+
@options = options.merge(:daemonize => true)
|
22
|
+
@size = @options.delete(:servers)
|
23
|
+
@script = 'thin'
|
24
|
+
end
|
25
|
+
|
26
|
+
def first_port; @options[:port] end
|
27
|
+
def address; @options[:address] end
|
28
|
+
def pid_file; File.expand_path File.join(@options[:chdir], @options[:pid]) end
|
29
|
+
def log_file; File.expand_path File.join(@options[:chdir], @options[:log]) end
|
30
|
+
|
31
|
+
# Start the servers
|
32
|
+
def start
|
33
|
+
with_each_server { |port| start_on_port port }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Start the server on a single port
|
37
|
+
def start_on_port(port)
|
38
|
+
log "Starting #{address}:#{port} ... "
|
39
|
+
|
40
|
+
run :start, @options, port
|
41
|
+
end
|
42
|
+
|
43
|
+
# Stop the servers
|
44
|
+
def stop
|
45
|
+
with_each_server { |port| stop_on_port port }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Stop the server running on +port+
|
49
|
+
def stop_on_port(port)
|
50
|
+
log "Stopping #{address}:#{port} ... "
|
51
|
+
|
52
|
+
run :stop, @options, port
|
53
|
+
end
|
54
|
+
|
55
|
+
# Stop and start the servers.
|
56
|
+
def restart
|
57
|
+
stop
|
58
|
+
sleep 0.1 # Let's breath a bit shall we ?
|
59
|
+
start
|
60
|
+
end
|
61
|
+
|
62
|
+
def log_file_for(port)
|
63
|
+
include_port_number log_file, port
|
64
|
+
end
|
65
|
+
|
66
|
+
def pid_file_for(port)
|
67
|
+
include_port_number pid_file, port
|
68
|
+
end
|
69
|
+
|
70
|
+
def pid_for(port)
|
71
|
+
File.read(pid_file_for(port)).chomp.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
# Send the command to the +thin+ script
|
76
|
+
def run(cmd, options, port)
|
77
|
+
shell_cmd = shellify(cmd, options.merge(:port => port, :pid => pid_file_for(port), :log => log_file_for(port)))
|
78
|
+
trace shell_cmd
|
79
|
+
ouput = `#{shell_cmd}`.chomp
|
80
|
+
log ouput unless ouput.empty?
|
81
|
+
end
|
82
|
+
|
83
|
+
# Turn into a runnable shell command
|
84
|
+
def shellify(cmd, options)
|
85
|
+
shellified_options = options.inject([]) do |args, (name, value)|
|
86
|
+
args << case value
|
87
|
+
when NilClass
|
88
|
+
when TrueClass then "--#{name}"
|
89
|
+
else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
"#{@script} #{cmd} #{shellified_options.compact.join(' ')}"
|
93
|
+
end
|
94
|
+
|
95
|
+
def with_each_server
|
96
|
+
@size.times { |n| yield first_port + n }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add the port numbers in the filename
|
100
|
+
# so each instance get its own file
|
101
|
+
def include_port_number(path, port)
|
102
|
+
raise ArgumentError, "filename '#{path}' must include an extension" unless path =~ /\./
|
103
|
+
path.gsub(/\.(.+)$/) { ".#{port}.#{$1}" }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/thin/connection.rb
CHANGED
@@ -12,11 +12,11 @@ module Thin
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def receive_data(data)
|
15
|
+
trace { data }
|
15
16
|
process if @request.parse(data)
|
16
17
|
rescue InvalidRequest => e
|
17
18
|
log "Invalid request"
|
18
19
|
log_error e
|
19
|
-
trace { data }
|
20
20
|
close_connection
|
21
21
|
end
|
22
22
|
|
@@ -30,9 +30,8 @@ module Thin
|
|
30
30
|
@response.status, @response.headers, @response.body = @app.call(env)
|
31
31
|
|
32
32
|
# Send the response
|
33
|
-
|
34
|
-
@response.
|
35
|
-
send_data @response.body.read
|
33
|
+
trace { @response.to_s }
|
34
|
+
send_data @response.to_s
|
36
35
|
|
37
36
|
close_connection_after_writing
|
38
37
|
|
data/lib/thin/daemonizing.rb
CHANGED
@@ -1,22 +1,5 @@
|
|
1
1
|
require 'etc'
|
2
|
-
|
3
|
-
module Kernel
|
4
|
-
unless respond_to? :daemonize # Already part of Ruby 1.9, yeah!
|
5
|
-
# Turns the current script into a daemon process that detaches from the console.
|
6
|
-
# It can be shut down with a TERM signal. Taken from ActiveSupport.
|
7
|
-
def daemonize
|
8
|
-
exit if fork # Parent exits, child continues.
|
9
|
-
Process.setsid # Become session leader.
|
10
|
-
exit if fork # Zap session leader. See [1].
|
11
|
-
Dir.chdir "/" # Release old working directory.
|
12
|
-
File.umask 0000 # Ensure sensible umask. Adjust as needed.
|
13
|
-
STDIN.reopen "/dev/null" # Free file descriptors and
|
14
|
-
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
|
15
|
-
STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
|
16
|
-
trap("TERM") { exit }
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
2
|
+
require 'daemons'
|
20
3
|
|
21
4
|
module Process
|
22
5
|
# Returns +true+ the process identied by +pid+ is running.
|
@@ -51,13 +34,10 @@ module Thin
|
|
51
34
|
raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
|
52
35
|
|
53
36
|
pwd = Dir.pwd # Current directory is changed during daemonization, so store it
|
54
|
-
super # Calls Kernel#daemonize
|
55
|
-
Dir.chdir pwd
|
56
37
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
[STDOUT, STDERR].each { |f| f.reopen @log_file, 'a' } if @log_file
|
38
|
+
Daemonize.daemonize(File.expand_path(@log_file))
|
39
|
+
|
40
|
+
Dir.chdir(pwd)
|
61
41
|
|
62
42
|
write_pid_file
|
63
43
|
at_exit do
|
data/lib/thin/request.rb
CHANGED
@@ -5,6 +5,7 @@ module Thin
|
|
5
5
|
# and the server can not process it.
|
6
6
|
class InvalidRequest < IOError; end
|
7
7
|
|
8
|
+
# A request sent by the client to the server.
|
8
9
|
class Request
|
9
10
|
MAX_HEADER = 1024 * (80 + 32)
|
10
11
|
MAX_HEADER_MSG = 'Header longer than allowed'.freeze
|
@@ -35,7 +36,7 @@ module Thin
|
|
35
36
|
# Rack stuff
|
36
37
|
RACK_INPUT => @body,
|
37
38
|
|
38
|
-
RACK_VERSION => [0,
|
39
|
+
RACK_VERSION => [0, 2],
|
39
40
|
RACK_ERRORS => STDERR,
|
40
41
|
|
41
42
|
RACK_MULTITHREAD => false,
|
@@ -47,18 +48,18 @@ module Thin
|
|
47
48
|
def parse(data)
|
48
49
|
@data << data
|
49
50
|
|
50
|
-
if @parser.finished?
|
51
|
+
if @parser.finished? # Header finished, can only be some more body
|
51
52
|
body << data
|
52
|
-
elsif @data.size > MAX_HEADER
|
53
|
+
elsif @data.size > MAX_HEADER # Oho! very big header, must be a mean person
|
53
54
|
raise InvalidRequest, MAX_HEADER_MSG
|
54
|
-
else
|
55
|
+
else # Parse more header
|
55
56
|
@nparsed = @parser.execute(@env, @data, @nparsed)
|
56
57
|
end
|
57
58
|
|
58
59
|
# Check if header and body are complete
|
59
60
|
if @parser.finished? && body.size >= content_length
|
60
61
|
body.rewind
|
61
|
-
return true
|
62
|
+
return true # Request is fully parsed
|
62
63
|
end
|
63
64
|
|
64
65
|
false # Not finished, need more data
|
data/lib/thin/response.rb
CHANGED
data/lib/thin/server.rb
CHANGED
@@ -4,9 +4,9 @@ module Thin
|
|
4
4
|
|
5
5
|
# The Thin HTTP server used to served request.
|
6
6
|
# It listen for incoming request on a given port
|
7
|
-
# and forward all request to
|
8
|
-
#
|
9
|
-
# Based on HTTP 1.1 protocol specs
|
7
|
+
# and forward all request to +app+.
|
8
|
+
#
|
9
|
+
# Based on HTTP 1.1 protocol specs:
|
10
10
|
# http://www.w3.org/Protocols/rfc2616/rfc2616.html
|
11
11
|
class Server
|
12
12
|
include Logging
|
@@ -18,12 +18,12 @@ module Thin
|
|
18
18
|
# App called with the request that produce the response.
|
19
19
|
attr_accessor :app
|
20
20
|
|
21
|
-
# Maximum time for
|
21
|
+
# Maximum time for incoming data to arrive
|
22
22
|
attr_accessor :timeout
|
23
23
|
|
24
24
|
# Creates a new server binded to <tt>host:port</tt>
|
25
25
|
# that will pass request to +app+.
|
26
|
-
def initialize(host, port, app)
|
26
|
+
def initialize(host, port, app=nil)
|
27
27
|
@host = host
|
28
28
|
@port = port.to_i
|
29
29
|
@app = app
|
@@ -32,7 +32,9 @@ module Thin
|
|
32
32
|
|
33
33
|
# Starts the handlers.
|
34
34
|
def start
|
35
|
-
|
35
|
+
raise ArgumentError, "app required" unless @app
|
36
|
+
|
37
|
+
log ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
|
36
38
|
trace ">> Tracing ON"
|
37
39
|
end
|
38
40
|
|
@@ -64,6 +66,7 @@ module Thin
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
69
|
+
|
67
70
|
def stop
|
68
71
|
EventMachine.stop_event_loop
|
69
72
|
rescue
|
data/lib/thin/version.rb
CHANGED
data/lib/thin_parser.bundle
CHANGED
Binary file
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Cluster do
|
4
|
+
before do
|
5
|
+
@cluster = Thin::Cluster.new(:chdir => File.dirname(__FILE__) + '/rails_app',
|
6
|
+
:address => '0.0.0.0',
|
7
|
+
:port => 3000,
|
8
|
+
:servers => 3,
|
9
|
+
:timeout => 10,
|
10
|
+
:log => 'thin.log',
|
11
|
+
:pid => 'thin.pid'
|
12
|
+
)
|
13
|
+
@cluster.script = File.dirname(__FILE__) + '/../bin/thin'
|
14
|
+
@cluster.silent = true
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should include port number in file names' do
|
18
|
+
@cluster.send(:include_port_number, 'thin.log', 3000).should == 'thin.3000.log'
|
19
|
+
@cluster.send(:include_port_number, 'thin.pid', 3000).should == 'thin.3000.pid'
|
20
|
+
proc { @cluster.send(:include_port_number, 'thin', 3000) }.should raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should call each server' do
|
24
|
+
calls = []
|
25
|
+
@cluster.send(:with_each_server) do |port|
|
26
|
+
calls << port
|
27
|
+
end
|
28
|
+
calls.should == [3000, 3001, 3002]
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should shellify command' do
|
32
|
+
out = @cluster.send(:shellify, :start, :port => 3000, :daemonize => true, :log => 'hi.log', :pid => nil)
|
33
|
+
out.should include('--port=3000', '--daemonize', '--log="hi.log"', 'thin start --')
|
34
|
+
out.should_not include('--pid=')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should absolutize file path' do
|
38
|
+
@cluster.pid_file_for(3000).should == File.expand_path(File.dirname(__FILE__) + "/rails_app/thin.3000.pid")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should start on specified port' do
|
42
|
+
@cluster.should_receive(:`) do |with|
|
43
|
+
with.should include('thin start', '--daemonize', 'thin.3001.log', 'thin.3001.pid', '--port=3001')
|
44
|
+
''
|
45
|
+
end
|
46
|
+
|
47
|
+
@cluster.start_on_port 3001
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should stop on specified port' do
|
51
|
+
@cluster.should_receive(:`) do |with|
|
52
|
+
with.should include('thin stop', '--daemonize', 'thin.3001.log', 'thin.3001.pid', '--port=3001')
|
53
|
+
''
|
54
|
+
end
|
55
|
+
|
56
|
+
@cluster.stop_on_port 3001
|
57
|
+
end
|
58
|
+
end
|