thin 0.5.3-x86-mswin32-60
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/CHANGELOG +40 -0
- data/COPYING +18 -0
- data/README +60 -0
- data/Rakefile +11 -0
- data/benchmark/simple.rb +48 -0
- data/bin/thin +123 -0
- data/doc/benchmarks.txt +86 -0
- data/doc/rdoc/classes/Process.html +181 -0
- data/doc/rdoc/classes/Rack.html +156 -0
- data/doc/rdoc/classes/Rack/Adapter.html +155 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
- data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
- data/doc/rdoc/classes/Rack/Handler.html +155 -0
- data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
- data/doc/rdoc/classes/Thin.html +164 -0
- data/doc/rdoc/classes/Thin/Cluster.html +399 -0
- data/doc/rdoc/classes/Thin/Connection.html +223 -0
- data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
- data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
- data/doc/rdoc/classes/Thin/Headers.html +238 -0
- data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
- data/doc/rdoc/classes/Thin/Logging.html +201 -0
- data/doc/rdoc/classes/Thin/Request.html +231 -0
- data/doc/rdoc/classes/Thin/Response.html +271 -0
- data/doc/rdoc/classes/Thin/Server.html +295 -0
- data/doc/rdoc/classes/Thin/StopServer.html +143 -0
- data/doc/rdoc/created.rid +1 -0
- data/doc/rdoc/files/README.html +226 -0
- data/doc/rdoc/files/bin/thin.html +245 -0
- data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
- data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
- data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
- data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
- data/doc/rdoc/files/lib/thin_rb.html +152 -0
- data/doc/rdoc/index.html +10 -0
- data/doc/rdoc/logo.gif +0 -0
- data/doc/rdoc/rdoc-style.css +55 -0
- data/example/config.ru +9 -0
- data/example/thin.god +72 -0
- data/ext/thin_parser/common.rl +54 -0
- data/ext/thin_parser/ext_help.h +14 -0
- data/ext/thin_parser/extconf.rb +6 -0
- data/ext/thin_parser/parser.c +1199 -0
- data/ext/thin_parser/parser.h +49 -0
- data/ext/thin_parser/parser.rl +143 -0
- data/ext/thin_parser/thin.c +424 -0
- data/lib/rack/adapter/rails.rb +155 -0
- data/lib/rack/handler/thin.rb +13 -0
- data/lib/thin.rb +36 -0
- data/lib/thin/cluster.rb +106 -0
- data/lib/thin/connection.rb +46 -0
- data/lib/thin/daemonizing.rb +112 -0
- data/lib/thin/headers.rb +37 -0
- data/lib/thin/logging.rb +23 -0
- data/lib/thin/request.rb +72 -0
- data/lib/thin/response.rb +48 -0
- data/lib/thin/server.rb +80 -0
- data/lib/thin/statuses.rb +43 -0
- data/lib/thin/version.rb +11 -0
- data/lib/thin_parser.so +0 -0
- data/spec/cluster_spec.rb +58 -0
- data/spec/daemonizing_spec.rb +93 -0
- data/spec/headers_spec.rb +35 -0
- data/spec/rack_rails_spec.rb +92 -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/request_spec.rb +258 -0
- data/spec/response_spec.rb +56 -0
- data/spec/server_spec.rb +75 -0
- data/spec/spec_helper.rb +127 -0
- metadata +219 -0
data/lib/thin/headers.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Thin
|
2
|
+
# Acts like a Hash, but allows duplicated keys
|
3
|
+
class Headers
|
4
|
+
HEADER_FORMAT = "%s: %s\r\n".freeze
|
5
|
+
ALLOWED_DUPLICATES = %w(Set-Cookie Set-Cookie2 Warning WWW-Authenticate).freeze
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@sent = {}
|
9
|
+
@items = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def []=(key, value)
|
13
|
+
if @sent.has_key?(key) && !ALLOWED_DUPLICATES.include?(key)
|
14
|
+
# If we don't allow duplicate for that field
|
15
|
+
# we overwrite the one that is already there
|
16
|
+
@items.assoc(key)[1] = value
|
17
|
+
else
|
18
|
+
@sent[key] = true
|
19
|
+
@items << [key, value]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
if item = @items.assoc(key)
|
25
|
+
item[1]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def size
|
30
|
+
@items.size
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
@items.inject('') { |out, (name, value)| out << HEADER_FORMAT % [name, value] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/thin/logging.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Thin
|
2
|
+
# To be included into classes to allow some basic logging
|
3
|
+
# that can be silented (+silent+) or made more verbose ($DEBUG=true).
|
4
|
+
module Logging
|
5
|
+
# Don't output any message if +true+.
|
6
|
+
attr_accessor :silent
|
7
|
+
|
8
|
+
protected
|
9
|
+
# Log a message to the console
|
10
|
+
def log(msg)
|
11
|
+
puts msg unless @silent
|
12
|
+
end
|
13
|
+
|
14
|
+
# Log a message to the console if tracing is activated
|
15
|
+
def trace(msg=nil)
|
16
|
+
puts msg || yield if $DEBUG && !@silent
|
17
|
+
end
|
18
|
+
|
19
|
+
def log_error(e)
|
20
|
+
trace { "#{e}\n\t" + e.backtrace.join("\n\t") }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/thin/request.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'thin_parser'
|
2
|
+
|
3
|
+
module Thin
|
4
|
+
# Raised when an incoming request is not valid
|
5
|
+
# and the server can not process it.
|
6
|
+
class InvalidRequest < IOError; end
|
7
|
+
|
8
|
+
# A request sent by the client to the server.
|
9
|
+
class Request
|
10
|
+
MAX_HEADER = 1024 * (80 + 32)
|
11
|
+
MAX_HEADER_MSG = 'Header longer than allowed'.freeze
|
12
|
+
|
13
|
+
SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
|
14
|
+
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
15
|
+
FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'.freeze
|
16
|
+
|
17
|
+
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
18
|
+
|
19
|
+
RACK_INPUT = 'rack.input'.freeze
|
20
|
+
RACK_VERSION = 'rack.version'.freeze
|
21
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
22
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
23
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
24
|
+
RACK_RUN_ONCE = 'rack.run_once'.freeze
|
25
|
+
|
26
|
+
attr_reader :env, :data, :body
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@parser = HttpParser.new
|
30
|
+
@data = ''
|
31
|
+
@nparsed = 0
|
32
|
+
@body = StringIO.new
|
33
|
+
@env = {
|
34
|
+
SERVER_SOFTWARE => SERVER,
|
35
|
+
|
36
|
+
# Rack stuff
|
37
|
+
RACK_INPUT => @body,
|
38
|
+
|
39
|
+
RACK_VERSION => [0, 2],
|
40
|
+
RACK_ERRORS => STDERR,
|
41
|
+
|
42
|
+
RACK_MULTITHREAD => false,
|
43
|
+
RACK_MULTIPROCESS => false,
|
44
|
+
RACK_RUN_ONCE => false
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse(data)
|
49
|
+
@data << data
|
50
|
+
|
51
|
+
if @parser.finished? # Header finished, can only be some more body
|
52
|
+
body << data
|
53
|
+
elsif @data.size > MAX_HEADER # Oho! very big header, must be a mean person
|
54
|
+
raise InvalidRequest, MAX_HEADER_MSG
|
55
|
+
else # Parse more header using the super parser
|
56
|
+
@nparsed = @parser.execute(@env, @data, @nparsed)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if header and body are complete
|
60
|
+
if @parser.finished? && body.size >= content_length
|
61
|
+
body.rewind
|
62
|
+
return true # Request is fully parsed
|
63
|
+
end
|
64
|
+
|
65
|
+
false # Not finished, need more data
|
66
|
+
end
|
67
|
+
|
68
|
+
def content_length
|
69
|
+
@env[CONTENT_LENGTH].to_i
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Thin
|
2
|
+
# A response sent to the client.
|
3
|
+
class Response
|
4
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
5
|
+
CONNECTION = 'Connection'.freeze
|
6
|
+
CLOSE = 'close'.freeze
|
7
|
+
|
8
|
+
attr_accessor :status
|
9
|
+
attr_reader :headers, :body
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@headers = Headers.new
|
13
|
+
@body = StringIO.new
|
14
|
+
@status = 200
|
15
|
+
end
|
16
|
+
|
17
|
+
def headers_output
|
18
|
+
@headers[CONTENT_LENGTH] = @body.size
|
19
|
+
@headers[CONNECTION] = CLOSE
|
20
|
+
@headers.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def head
|
24
|
+
"HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def headers=(key_value_pairs)
|
28
|
+
key_value_pairs.each do |k, vs|
|
29
|
+
vs.each { |v| @headers[k] = v.chomp }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def body=(stream)
|
34
|
+
stream.each do |part|
|
35
|
+
@body << part
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def close
|
40
|
+
@body.close
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
@body.rewind
|
45
|
+
head + @body.read
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/thin/server.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Thin
|
2
|
+
# Raise when we require the server to stop
|
3
|
+
class StopServer < Exception; end
|
4
|
+
|
5
|
+
# The Thin HTTP server used to served request.
|
6
|
+
# It listen for incoming request on a given port
|
7
|
+
# and forward all request to +app+.
|
8
|
+
#
|
9
|
+
# Based on HTTP 1.1 protocol specs:
|
10
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616.html
|
11
|
+
class Server
|
12
|
+
include Logging
|
13
|
+
include Daemonizable
|
14
|
+
|
15
|
+
# Addresse and port on which the server is listening for connections.
|
16
|
+
attr_accessor :port, :host
|
17
|
+
|
18
|
+
# App called with the request that produce the response.
|
19
|
+
attr_accessor :app
|
20
|
+
|
21
|
+
# Maximum time for incoming data to arrive
|
22
|
+
attr_accessor :timeout
|
23
|
+
|
24
|
+
# Creates a new server binded to <tt>host:port</tt>
|
25
|
+
# that will pass request to +app+.
|
26
|
+
def initialize(host, port, app=nil)
|
27
|
+
@host = host
|
28
|
+
@port = port.to_i
|
29
|
+
@app = app
|
30
|
+
@timeout = 60 # sec
|
31
|
+
end
|
32
|
+
|
33
|
+
# Starts the handlers.
|
34
|
+
def start
|
35
|
+
raise ArgumentError, "app required" unless @app
|
36
|
+
|
37
|
+
log ">> Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
|
38
|
+
trace ">> Tracing ON"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Start the server and listen for connections
|
42
|
+
def start!
|
43
|
+
start
|
44
|
+
listen!
|
45
|
+
end
|
46
|
+
|
47
|
+
# Start listening for connections
|
48
|
+
def listen!
|
49
|
+
trap('INT') { stop }
|
50
|
+
trap('TERM') { stop! }
|
51
|
+
|
52
|
+
# See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
|
53
|
+
EventMachine.epoll
|
54
|
+
|
55
|
+
EventMachine.run do
|
56
|
+
begin
|
57
|
+
log ">> Listening on #{@host}:#{@port}, CTRL+C to stop"
|
58
|
+
EventMachine.start_server(@host, @port, Connection) do |connection|
|
59
|
+
connection.comm_inactivity_timeout = @timeout
|
60
|
+
connection.app = @app
|
61
|
+
connection.silent = @silent
|
62
|
+
end
|
63
|
+
rescue StopServer
|
64
|
+
EventMachine.stop_event_loop
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def stop
|
71
|
+
EventMachine.stop_event_loop
|
72
|
+
rescue
|
73
|
+
warn "Error stopping : #{$!}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def stop!
|
77
|
+
raise StopServer
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Thin
|
2
|
+
# Every standard HTTP code mapped to the appropriate message.
|
3
|
+
# Stolent from Mongrel.
|
4
|
+
HTTP_STATUS_CODES = {
|
5
|
+
100 => 'Continue',
|
6
|
+
101 => 'Switching Protocols',
|
7
|
+
200 => 'OK',
|
8
|
+
201 => 'Created',
|
9
|
+
202 => 'Accepted',
|
10
|
+
203 => 'Non-Authoritative Information',
|
11
|
+
204 => 'No Content',
|
12
|
+
205 => 'Reset Content',
|
13
|
+
206 => 'Partial Content',
|
14
|
+
300 => 'Multiple Choices',
|
15
|
+
301 => 'Moved Permanently',
|
16
|
+
302 => 'Moved Temporarily',
|
17
|
+
303 => 'See Other',
|
18
|
+
304 => 'Not Modified',
|
19
|
+
305 => 'Use Proxy',
|
20
|
+
400 => 'Bad Request',
|
21
|
+
401 => 'Unauthorized',
|
22
|
+
402 => 'Payment Required',
|
23
|
+
403 => 'Forbidden',
|
24
|
+
404 => 'Not Found',
|
25
|
+
405 => 'Method Not Allowed',
|
26
|
+
406 => 'Not Acceptable',
|
27
|
+
407 => 'Proxy Authentication Required',
|
28
|
+
408 => 'Request Time-out',
|
29
|
+
409 => 'Conflict',
|
30
|
+
410 => 'Gone',
|
31
|
+
411 => 'Length Required',
|
32
|
+
412 => 'Precondition Failed',
|
33
|
+
413 => 'Request Entity Too Large',
|
34
|
+
414 => 'Request-URI Too Large',
|
35
|
+
415 => 'Unsupported Media Type',
|
36
|
+
500 => 'Internal Server Error',
|
37
|
+
501 => 'Not Implemented',
|
38
|
+
502 => 'Bad Gateway',
|
39
|
+
503 => 'Service Unavailable',
|
40
|
+
504 => 'Gateway Time-out',
|
41
|
+
505 => 'HTTP Version not supported'
|
42
|
+
}
|
43
|
+
end
|
data/lib/thin/version.rb
ADDED
data/lib/thin_parser.so
ADDED
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
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe 'Daemonizing' do
|
4
|
+
before do
|
5
|
+
@server = Server.new('0.0.0.0', 3000, nil)
|
6
|
+
@server.log_file = File.dirname(__FILE__) + '/../log/daemonizing_test.log'
|
7
|
+
@server.pid_file = 'test.pid'
|
8
|
+
@pid = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should have a pid file' do
|
12
|
+
@server.should respond_to(:pid_file)
|
13
|
+
@server.should respond_to(:pid_file=)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should create a pid file' do
|
17
|
+
@pid = fork do
|
18
|
+
@server.daemonize
|
19
|
+
sleep 1
|
20
|
+
end
|
21
|
+
|
22
|
+
Process.wait(@pid)
|
23
|
+
File.exist?(@server.pid_file).should be_true
|
24
|
+
@pid = @server.pid
|
25
|
+
|
26
|
+
proc { sleep 0.1 while File.exist?(@server.pid_file) }.should take_less_then(2)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should redirect stdio to a log file' do
|
30
|
+
@pid = fork do
|
31
|
+
@server.log_file = 'daemon_test.log'
|
32
|
+
@server.daemonize
|
33
|
+
|
34
|
+
puts "simple puts"
|
35
|
+
STDERR.puts "STDERR.puts"
|
36
|
+
STDOUT.puts "STDOUT.puts"
|
37
|
+
end
|
38
|
+
Process.wait(@pid)
|
39
|
+
# Wait for the file to close and magical stuff to happen
|
40
|
+
proc { sleep 0.1 until File.exist?('daemon_test.log') }.should take_less_then(3)
|
41
|
+
sleep 0.5
|
42
|
+
|
43
|
+
log = File.read('daemon_test.log')
|
44
|
+
log.should include('simple puts', 'STDERR.puts', 'STDOUT.puts')
|
45
|
+
|
46
|
+
File.delete 'daemon_test.log'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should change privilege' do
|
50
|
+
@pid = fork do
|
51
|
+
@server.daemonize
|
52
|
+
@server.change_privilege('root', 'admin')
|
53
|
+
end
|
54
|
+
Process.wait(@pid)
|
55
|
+
$?.should be_a_success
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should kill process in pid file' do
|
59
|
+
@pid = fork do
|
60
|
+
@server.daemonize
|
61
|
+
loop { sleep 1 }
|
62
|
+
end
|
63
|
+
|
64
|
+
proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(3)
|
65
|
+
|
66
|
+
silence_stream STDOUT do
|
67
|
+
Server.kill(@server.pid_file, 1)
|
68
|
+
end
|
69
|
+
|
70
|
+
File.exist?(@server.pid_file).should_not be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should send kill signal if timeout' do
|
74
|
+
@pid = fork do
|
75
|
+
@server.should_receive(:stop) # pretend we cannot handle the INT signal
|
76
|
+
@server.daemonize
|
77
|
+
sleep 5
|
78
|
+
end
|
79
|
+
|
80
|
+
proc { sleep 0.1 until File.exist?(@server.pid_file) }.should take_less_then(10)
|
81
|
+
|
82
|
+
silence_stream STDOUT do
|
83
|
+
Server.kill(@server.pid_file, 1)
|
84
|
+
end
|
85
|
+
|
86
|
+
File.exist?(@server.pid_file).should be_false
|
87
|
+
Process.running?(@pid).should be_false
|
88
|
+
end
|
89
|
+
|
90
|
+
after do
|
91
|
+
Process.kill(9, @pid.to_i) if @pid && Process.running?(@pid.to_i)
|
92
|
+
end
|
93
|
+
end
|