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.
- data/.gitignore +9 -0
- data/CHANGELOG +29 -107
- data/Gemfile +8 -0
- data/README.md +44 -78
- data/Rakefile +28 -18
- data/bin/thin +4 -4
- data/examples/async.ru +21 -0
- data/examples/thin.conf.rb +39 -0
- data/lib/thin.rb +2 -44
- data/lib/thin/async.rb +108 -0
- data/lib/thin/backends/prefork.rb +44 -0
- data/lib/thin/backends/single_process.rb +28 -0
- data/lib/thin/chunked_body.rb +28 -0
- data/lib/thin/configurator.rb +118 -0
- data/lib/thin/connection.rb +246 -172
- data/lib/thin/listener.rb +114 -0
- data/lib/thin/request.rb +94 -76
- data/lib/thin/response.rb +112 -45
- data/lib/thin/runner.rb +134 -197
- data/lib/thin/server.rb +203 -252
- data/lib/thin/system.rb +49 -0
- data/lib/thin/version.rb +11 -26
- data/man/index.txt +3 -0
- data/man/thin-conf.5.ronn +121 -0
- data/man/thin.1.ronn +105 -0
- data/site/.gitignore +2 -0
- data/site/README.md +21 -0
- data/site/Rakefile +20 -0
- data/site/config.ru +4 -0
- data/site/public/images/grid.png +0 -0
- data/site/public/javascripts/dd_belatedpng.js +13 -0
- data/site/public/javascripts/modernizr-1.6.min.js +30 -0
- data/site/public/man/thin-conf.5.html +220 -0
- data/site/public/man/thin.1.html +177 -0
- data/site/site/assets/javascripts/main.coffee +2 -0
- data/site/site/assets/stylesheets/_config.scss +55 -0
- data/site/site/assets/stylesheets/main.scss +24 -0
- data/site/site/helpers.rb +17 -0
- data/site/site/layouts/base.erb +55 -0
- data/site/site/layouts/default.erb +17 -0
- data/site/site/pages/about.md +5 -0
- data/site/site/pages/index.erb +10 -0
- data/site/site/partials/.gitkeep +0 -0
- data/test/fixtures/big.txt +1 -0
- data/test/fixtures/small.txt +1 -0
- data/test/fixtures/thin.conf.rb +15 -0
- data/test/integration/async_test.rb +35 -0
- data/test/integration/big_request_test.rb +30 -0
- data/test/integration/config.ru +57 -0
- data/test/integration/daemonize_test.rb +26 -0
- data/test/integration/env_test.rb +44 -0
- data/test/integration/error_test.rb +37 -0
- data/test/integration/file_sending_test.rb +24 -0
- data/test/integration/keep_alive_test.rb +35 -0
- data/test/integration/robustness_test.rb +37 -0
- data/test/integration/single_process_test.rb +15 -0
- data/test/integration/socket_family_test.rb +38 -0
- data/test/integration/worker_test.rb +22 -0
- data/test/test_helper.rb +195 -0
- data/test/unit/configurator_test.rb +43 -0
- data/test/unit/connection_test.rb +94 -0
- data/test/unit/listener_test.rb +74 -0
- data/test/unit/request_test.rb +74 -0
- data/test/unit/response_test.rb +90 -0
- data/test/unit/server_test.rb +29 -0
- data/test/unit/system_test.rb +17 -0
- data/thin.gemspec +26 -0
- data/v2.todo +21 -0
- metadata +138 -93
- checksums.yaml +0 -7
- data/example/adapter.rb +0 -32
- data/example/async_app.ru +0 -126
- data/example/async_chat.ru +0 -247
- data/example/async_tailer.ru +0 -100
- data/example/config.ru +0 -22
- data/example/monit_sockets +0 -20
- data/example/monit_unixsock +0 -20
- data/example/myapp.rb +0 -1
- data/example/ramaze.ru +0 -12
- data/example/thin.god +0 -80
- data/example/thin_solaris_smf.erb +0 -36
- data/example/thin_solaris_smf.readme.txt +0 -150
- data/example/vlad.rake +0 -72
- data/ext/thin_parser/common.rl +0 -59
- data/ext/thin_parser/ext_help.h +0 -14
- data/ext/thin_parser/extconf.rb +0 -6
- data/ext/thin_parser/parser.c +0 -1447
- data/ext/thin_parser/parser.h +0 -49
- data/ext/thin_parser/parser.rl +0 -152
- data/ext/thin_parser/thin.c +0 -435
- data/lib/rack/adapter/loader.rb +0 -75
- data/lib/rack/adapter/rails.rb +0 -178
- data/lib/rack/handler/thin.rb +0 -38
- data/lib/thin/backends/base.rb +0 -169
- data/lib/thin/backends/swiftiply_client.rb +0 -56
- data/lib/thin/backends/tcp_server.rb +0 -34
- data/lib/thin/backends/unix_server.rb +0 -56
- data/lib/thin/command.rb +0 -53
- data/lib/thin/controllers/cluster.rb +0 -178
- data/lib/thin/controllers/controller.rb +0 -189
- data/lib/thin/controllers/service.rb +0 -76
- data/lib/thin/controllers/service.sh.erb +0 -39
- data/lib/thin/daemonizing.rb +0 -199
- data/lib/thin/headers.rb +0 -40
- data/lib/thin/logging.rb +0 -174
- data/lib/thin/stats.html.erb +0 -216
- data/lib/thin/stats.rb +0 -52
- data/lib/thin/statuses.rb +0 -48
@@ -0,0 +1,57 @@
|
|
1
|
+
require "thin/async"
|
2
|
+
|
3
|
+
class App
|
4
|
+
def call(env)
|
5
|
+
request = Rack::Request.new(env)
|
6
|
+
|
7
|
+
case request.path_info
|
8
|
+
when "/"
|
9
|
+
Rack::Response.new do |response|
|
10
|
+
response.write "ok"
|
11
|
+
end.finish
|
12
|
+
|
13
|
+
when "/env"
|
14
|
+
Rack::Response.new do |response|
|
15
|
+
env.each_pair do |key, value|
|
16
|
+
response.write "#{key}: #{value}\n"
|
17
|
+
end
|
18
|
+
response.write "\n" + request.body.read
|
19
|
+
end.finish
|
20
|
+
|
21
|
+
when "/eval"
|
22
|
+
Rack::Response.new do |response|
|
23
|
+
response.write eval(request["code"]).to_s
|
24
|
+
end.finish
|
25
|
+
|
26
|
+
when "/raise"
|
27
|
+
raise "ouch"
|
28
|
+
|
29
|
+
when "/exit"
|
30
|
+
exit!
|
31
|
+
|
32
|
+
when "/sleep"
|
33
|
+
sleep request["sec"].to_f
|
34
|
+
|
35
|
+
when "/async"
|
36
|
+
Thin::AsyncResponse.new(env) do |response|
|
37
|
+
response << "one\n"
|
38
|
+
EM.next_tick do
|
39
|
+
response << "two\n"
|
40
|
+
response.done # close the connection
|
41
|
+
end
|
42
|
+
end.finish
|
43
|
+
|
44
|
+
else
|
45
|
+
Rack::Response.new do |response|
|
46
|
+
response.status = 404
|
47
|
+
response.write "not found"
|
48
|
+
end.finish
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
use Rack::Static, :urls => ["/small.txt", "/big.txt"],
|
55
|
+
:root => File.expand_path("../../fixtures", __FILE__)
|
56
|
+
|
57
|
+
run App.new
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DaemonizeTest < IntegrationTestCase
|
4
|
+
def test_do_not_daemonize
|
5
|
+
pid = thin
|
6
|
+
|
7
|
+
assert_equal @pid, pid, "Launcher PID should be the same as master PID"
|
8
|
+
Process.kill 0, @pid
|
9
|
+
|
10
|
+
get "/"
|
11
|
+
|
12
|
+
assert_status 200
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_daemonize
|
16
|
+
pid = thin :daemonize => true
|
17
|
+
|
18
|
+
assert_not_equal @pid, pid, "Launcher PID should not be the same as master PID"
|
19
|
+
Process.kill 0, pid
|
20
|
+
Process.kill 0, @pid
|
21
|
+
|
22
|
+
get "/"
|
23
|
+
|
24
|
+
assert_status 200
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class EnvTest < IntegrationTestCase
|
4
|
+
def test_get
|
5
|
+
thin
|
6
|
+
|
7
|
+
get '/env?hi=there'
|
8
|
+
|
9
|
+
assert_status 200
|
10
|
+
assert_response_includes "REMOTE_ADDR: 127.0.0.1"
|
11
|
+
assert_response_includes "SERVER_SOFTWARE: thin"
|
12
|
+
assert_response_includes "HTTP_HOST: localhost:8181"
|
13
|
+
assert_response_includes "SERVER_PORT: 8181"
|
14
|
+
assert_response_includes "SERVER_NAME: localhost"
|
15
|
+
assert_response_includes "REQUEST_METHOD: GET"
|
16
|
+
assert_response_includes "PATH_INFO: /env"
|
17
|
+
assert_response_includes "QUERY_STRING: hi=there"
|
18
|
+
assert_response_includes "rack.url_scheme: http"
|
19
|
+
assert_response_includes "rack.multithread: false"
|
20
|
+
assert_response_includes "rack.multiprocess: true"
|
21
|
+
assert_response_includes "rack.run_once: false"
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_post
|
25
|
+
thin
|
26
|
+
|
27
|
+
post '/env', :hi => "there"
|
28
|
+
|
29
|
+
assert_status 200
|
30
|
+
assert_response_includes "REMOTE_ADDR: 127.0.0.1"
|
31
|
+
assert_response_includes "SERVER_SOFTWARE: thin"
|
32
|
+
assert_response_includes "HTTP_HOST: localhost:8181"
|
33
|
+
assert_response_includes "SERVER_PORT: 8181"
|
34
|
+
assert_response_includes "SERVER_NAME: localhost"
|
35
|
+
assert_response_includes "REQUEST_METHOD: POST"
|
36
|
+
assert_response_includes "PATH_INFO: /env"
|
37
|
+
assert_response_includes "QUERY_STRING: \n"
|
38
|
+
assert_response_includes "rack.url_scheme: http"
|
39
|
+
assert_response_includes "rack.multithread: false"
|
40
|
+
assert_response_includes "rack.multiprocess: true"
|
41
|
+
assert_response_includes "rack.run_once: false"
|
42
|
+
assert_response_includes "\n\nhi=there"
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ErrorTest < IntegrationTestCase
|
4
|
+
def test_raise
|
5
|
+
thin :log => "/dev/null"
|
6
|
+
|
7
|
+
get "/raise"
|
8
|
+
|
9
|
+
assert_status 500
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_raise_without_middlewares
|
13
|
+
thin :env => "none", :log => "/dev/null"
|
14
|
+
|
15
|
+
get "/raise"
|
16
|
+
|
17
|
+
assert_status 500
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_logs_errors
|
21
|
+
thin :env => "none"
|
22
|
+
|
23
|
+
get "/raise"
|
24
|
+
assert_match "[ERROR] ouch", read_log
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_parse_error
|
28
|
+
thin :log => "/dev/null"
|
29
|
+
|
30
|
+
socket do |s|
|
31
|
+
s.write "!!!WTH??YO111!\r\n"
|
32
|
+
s.flush
|
33
|
+
|
34
|
+
assert_match "HTTP/1.1 400 Bad Request", s.read
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FileSendingTest < IntegrationTestCase
|
4
|
+
def test_small_file
|
5
|
+
thin :env => "none"
|
6
|
+
|
7
|
+
get "/small.txt"
|
8
|
+
|
9
|
+
assert_status 200
|
10
|
+
assert_header "Transfer-Encoding", "chunked"
|
11
|
+
assert_equal File.size(File.expand_path("../../fixtures/small.txt", __FILE__)), @response.body.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_big_file
|
15
|
+
thin :env => "none"
|
16
|
+
|
17
|
+
# Just big enough (>16K) to trigger EM mapped streamer.
|
18
|
+
get "/big.txt"
|
19
|
+
|
20
|
+
assert_status 200
|
21
|
+
assert_header "Transfer-Encoding", "chunked"
|
22
|
+
assert_equal File.size(File.expand_path("../../fixtures/big.txt", __FILE__)), @response.body.size
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class KeepAliveTest < IntegrationTestCase
|
4
|
+
def test_enabled_by_default_on_http_1_1
|
5
|
+
thin
|
6
|
+
|
7
|
+
get '/'
|
8
|
+
|
9
|
+
assert_status 200
|
10
|
+
assert_header "Connection", "keep-alive"
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_disabled_on_http_1_0
|
14
|
+
thin
|
15
|
+
|
16
|
+
socket do |s|
|
17
|
+
s.write("GET / HTTP/1.0\r\n")
|
18
|
+
s.write("\r\n")
|
19
|
+
s.flush
|
20
|
+
|
21
|
+
assert_match "Connection: close", s.readpartial(1024)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_limited
|
26
|
+
thin do
|
27
|
+
keep_alive_requests 0
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/'
|
31
|
+
|
32
|
+
assert_status 200
|
33
|
+
assert_header "Connection", "close"
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class RobustnessTest < IntegrationTestCase
|
4
|
+
def test_should_not_crash_when_header_too_large
|
5
|
+
thin :log => "/dev/null"
|
6
|
+
|
7
|
+
100.times do
|
8
|
+
begin
|
9
|
+
socket do |s|
|
10
|
+
s.write("GET / HTTP/1.1\r\n")
|
11
|
+
s.write("Host: localhost\r\n")
|
12
|
+
s.write("Connection: close\r\n")
|
13
|
+
10000.times do
|
14
|
+
s.write("X-Foo: #{'x' * 100}\r\n")
|
15
|
+
s.flush
|
16
|
+
end
|
17
|
+
s.write("\r\n")
|
18
|
+
s.read
|
19
|
+
end
|
20
|
+
rescue Errno::EPIPE, Errno::ECONNRESET
|
21
|
+
# Ignore.
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_incomplete_request
|
27
|
+
thin do
|
28
|
+
timeout 1
|
29
|
+
end
|
30
|
+
|
31
|
+
socket do |s|
|
32
|
+
s.write "GET /?this HTTP/1.1\r\n"
|
33
|
+
s.write "Host:"
|
34
|
+
assert_equal "", s.read
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SingleProcessTest < IntegrationTestCase
|
4
|
+
def test_stop_with_int_signal
|
5
|
+
thin :env => "none" do
|
6
|
+
worker_processes 0
|
7
|
+
end
|
8
|
+
|
9
|
+
Process.kill "INT", @pid
|
10
|
+
_, status = Process.wait2 @pid
|
11
|
+
@pid = nil
|
12
|
+
|
13
|
+
assert status.success?
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SocketFamilyTest < IntegrationTestCase
|
4
|
+
def test_ipv4
|
5
|
+
thin do
|
6
|
+
listen PORT
|
7
|
+
end
|
8
|
+
|
9
|
+
get "/"
|
10
|
+
|
11
|
+
assert_status 200
|
12
|
+
assert_response_equals "ok"
|
13
|
+
assert_header "Content-Type", "text/html"
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_ipv6
|
17
|
+
thin do
|
18
|
+
listen "[::]:#{PORT}"
|
19
|
+
end
|
20
|
+
|
21
|
+
get "/"
|
22
|
+
|
23
|
+
assert_status 200
|
24
|
+
assert_response_equals "ok"
|
25
|
+
assert_header "Content-Type", "text/html"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_unix_socket
|
29
|
+
thin do
|
30
|
+
listen UNIX_SOCKET
|
31
|
+
end
|
32
|
+
|
33
|
+
unix_socket do |s|
|
34
|
+
s.write "GET / HTTP/1.1\r\n\r\n"
|
35
|
+
assert_match "HTTP/1.1 200 OK", s.read
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class WorkerTest < IntegrationTestCase
|
4
|
+
def test_restart_worker_on_exit
|
5
|
+
thin do
|
6
|
+
worker_processes 1
|
7
|
+
end
|
8
|
+
|
9
|
+
assert_raise(EOFError) { get "/exit" }
|
10
|
+
get "/"
|
11
|
+
|
12
|
+
assert_status 200
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_timeout
|
16
|
+
thin do
|
17
|
+
timeout 1
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_raise(EOFError) { get "/sleep?sec=2" }
|
21
|
+
end
|
22
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
Bundler.require(:default, :test)
|
3
|
+
|
4
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
5
|
+
require "thin"
|
6
|
+
require "test/unit"
|
7
|
+
require "mocha"
|
8
|
+
require "net/http"
|
9
|
+
require "timeout"
|
10
|
+
|
11
|
+
class Test::Unit::TestCase
|
12
|
+
include Mocha::API # fix mocha API not being included in minitest
|
13
|
+
|
14
|
+
# Silences any stream for the duration of the block.
|
15
|
+
#
|
16
|
+
# silence_stream(STDOUT) do
|
17
|
+
# puts 'This will never be seen'
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# puts 'But this will'
|
21
|
+
#
|
22
|
+
# (Taken from ActiveSupport)
|
23
|
+
def silence_stream(stream)
|
24
|
+
old_stream = stream.dup
|
25
|
+
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
26
|
+
stream.sync = true
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
stream.reopen(old_stream)
|
30
|
+
end
|
31
|
+
|
32
|
+
def silence_streams
|
33
|
+
silence_stream($stdout) { silence_stream($stderr) { yield } }
|
34
|
+
end
|
35
|
+
|
36
|
+
def capture_streams
|
37
|
+
out = StringIO.new
|
38
|
+
silence_streams do
|
39
|
+
$stdout = out
|
40
|
+
$stderr = out
|
41
|
+
yield
|
42
|
+
out.read
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
$stdout = STDOUT
|
46
|
+
$stderr = STDERR
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ConfigWriter < BasicObject
|
51
|
+
def initialize(&block)
|
52
|
+
@lines = []
|
53
|
+
instance_eval(&block) if block
|
54
|
+
end
|
55
|
+
|
56
|
+
def method_missing(name, *args)
|
57
|
+
@lines << name.to_s + " " + args.map { |arg| arg.inspect }.join(", ")
|
58
|
+
end
|
59
|
+
|
60
|
+
def write(file)
|
61
|
+
::File.open(file, "w") do |f|
|
62
|
+
f << "# Generated during tests by ConfigWriter in test_helper.rb\n"
|
63
|
+
f << @lines.join("\n")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class IntegrationTestCase < Test::Unit::TestCase
|
69
|
+
PORT = 8181
|
70
|
+
UNIX_SOCKET = "/tmp/thin-test.sock"
|
71
|
+
LOG_FILE = "test.log"
|
72
|
+
PID_FILE = "test.pid"
|
73
|
+
|
74
|
+
# Start a new thin server from the command line utility mimicing real world usage.
|
75
|
+
# @param runner_options Options to pass to the command line utility.
|
76
|
+
# @param configuration Block of configuration to pass to the configurator.
|
77
|
+
def thin(runner_options={}, &configuration)
|
78
|
+
raise "Server already started in process #" + File.read(PID_FILE) if File.exist?(PID_FILE)
|
79
|
+
|
80
|
+
# Cleanup
|
81
|
+
File.delete LOG_FILE if File.exist?(LOG_FILE)
|
82
|
+
File.delete UNIX_SOCKET if File.exist?(UNIX_SOCKET)
|
83
|
+
|
84
|
+
root = File.expand_path('../..', __FILE__)
|
85
|
+
|
86
|
+
# Generate a config file from the configuration block
|
87
|
+
config_file = "test.conf.rb"
|
88
|
+
config = ConfigWriter.new do
|
89
|
+
worker_processes 1
|
90
|
+
end
|
91
|
+
config.instance_eval(&configuration) if configuration
|
92
|
+
config.write(config_file)
|
93
|
+
|
94
|
+
# Command line options
|
95
|
+
runner_options = { :config => config_file, :port => PORT, :log => LOG_FILE, :pid => PID_FILE }.merge(runner_options)
|
96
|
+
|
97
|
+
# Launch the server from the shell
|
98
|
+
command = "bundle exec ruby -I#{root}/lib " +
|
99
|
+
"#{root}/bin/thin " +
|
100
|
+
runner_options.map { |k, v| (k.size > 1 ? "-" : "") + "-#{k}" + (TrueClass === v ? "" : "=#{v}") }.join(" ") + " " +
|
101
|
+
File.expand_path("../integration/config.ru", __FILE__)
|
102
|
+
launcher_pid = silence_stream($stdout) { spawn command }
|
103
|
+
|
104
|
+
tries = 0
|
105
|
+
wait = 5 #sec
|
106
|
+
until running?
|
107
|
+
sleep 0.1
|
108
|
+
tries += 1
|
109
|
+
raise "Failed to start server under #{wait} sec" if tries > wait/0.1
|
110
|
+
end
|
111
|
+
|
112
|
+
@pid = File.read(PID_FILE).to_i
|
113
|
+
|
114
|
+
launcher_pid
|
115
|
+
end
|
116
|
+
|
117
|
+
def teardown
|
118
|
+
if @pid
|
119
|
+
Process.kill "INT", @pid
|
120
|
+
begin
|
121
|
+
Process.wait @pid
|
122
|
+
rescue Errno::ECHILD
|
123
|
+
# Process is not a child. We ping until process dies.
|
124
|
+
sleep 0.1 while Process.kill 0, @pid rescue false
|
125
|
+
end
|
126
|
+
@pid = nil
|
127
|
+
end
|
128
|
+
raise "Didn't delete PID file." if File.exist?(PID_FILE)
|
129
|
+
@response = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def running?
|
133
|
+
return true if File.exist?(UNIX_SOCKET)
|
134
|
+
get("/")
|
135
|
+
true
|
136
|
+
rescue Errno::ECONNREFUSED
|
137
|
+
false
|
138
|
+
rescue
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
def get(path, host="localhost")
|
143
|
+
@response = Timeout.timeout(3) { Net::HTTP.get_response(URI.parse("http://#{host}:#{PORT}" + path)) }
|
144
|
+
end
|
145
|
+
|
146
|
+
def post(path, params={})
|
147
|
+
Timeout.timeout(3) do
|
148
|
+
uri = URI.parse("http://localhost:#{PORT}" + path)
|
149
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
150
|
+
|
151
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
152
|
+
request.set_form_data(params)
|
153
|
+
|
154
|
+
@response = http.request(request)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def socket
|
159
|
+
@response = nil
|
160
|
+
socket = TCPSocket.new("localhost", PORT)
|
161
|
+
yield socket
|
162
|
+
ensure
|
163
|
+
socket.close rescue nil
|
164
|
+
end
|
165
|
+
|
166
|
+
def unix_socket
|
167
|
+
@response = nil
|
168
|
+
socket = UNIXSocket.new(UNIX_SOCKET)
|
169
|
+
yield socket
|
170
|
+
ensure
|
171
|
+
socket.close rescue nil
|
172
|
+
end
|
173
|
+
|
174
|
+
def assert_status(status)
|
175
|
+
assert_equal status, @response.code.to_i
|
176
|
+
end
|
177
|
+
|
178
|
+
def assert_response_equals(string)
|
179
|
+
assert_equal string, @response.body
|
180
|
+
end
|
181
|
+
|
182
|
+
def assert_response_includes(*strings)
|
183
|
+
strings.each do |string|
|
184
|
+
assert @response.body.include?(string), "expected response to include #{string}, but got: #{@response.body}"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def assert_header(key, value)
|
189
|
+
assert_equal value, @response[key]
|
190
|
+
end
|
191
|
+
|
192
|
+
def read_log
|
193
|
+
File.read(LOG_FILE)
|
194
|
+
end
|
195
|
+
end
|