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
data/bin/thin
CHANGED
data/examples/async.ru
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "thin/async"
|
2
|
+
|
3
|
+
class Async
|
4
|
+
def call(env)
|
5
|
+
response = Thin::AsyncResponse.new(env)
|
6
|
+
|
7
|
+
# Webkit requires some padding before displaying something.
|
8
|
+
response << " " * 1024
|
9
|
+
|
10
|
+
response << "this is ... "
|
11
|
+
# Will be sent to the browse 1 sec after.
|
12
|
+
EM.add_timer(1) do
|
13
|
+
response << "async stuff!"
|
14
|
+
response.done # close the connection
|
15
|
+
end
|
16
|
+
|
17
|
+
response.finish
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
run Async.new
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Event backend options
|
2
|
+
worker_connections 1024
|
3
|
+
# Disabling epoll or kqueue will fallback to select.
|
4
|
+
# use_epoll false
|
5
|
+
# use_kqueue false
|
6
|
+
|
7
|
+
# Threading
|
8
|
+
# For slow apps, you can enable the use of threads.
|
9
|
+
# If you're using this in Rails, make sure to call config.threadsafe!
|
10
|
+
threaded true # Call the app in a thread.
|
11
|
+
thread_pool_size 20
|
12
|
+
|
13
|
+
# Worker options
|
14
|
+
# worker_processes 0 # runs in a single process w/ limited features.
|
15
|
+
worker_processes 4
|
16
|
+
timeout 30 # seconds
|
17
|
+
|
18
|
+
# Preload the app (the .ru file) before forking to workers.
|
19
|
+
# Enable with copy-on-write garbage collection for better memory usage.
|
20
|
+
preload_app true
|
21
|
+
|
22
|
+
# Logging
|
23
|
+
log_path "./thin.log"
|
24
|
+
pid_path "./thin.pid"
|
25
|
+
|
26
|
+
# Listeners
|
27
|
+
listen 3000, :backlog => 1024, :tcp_no_delay => true
|
28
|
+
listen "0.0.0.0:8080"
|
29
|
+
listen "[::]:8081" # IPv6
|
30
|
+
listen "/tmp/thin.sock" # UNIX domain socket
|
31
|
+
|
32
|
+
# Callbacks
|
33
|
+
before_fork do |server|
|
34
|
+
puts "Preparing to fork a new worker ..."
|
35
|
+
end
|
36
|
+
|
37
|
+
after_fork do |server|
|
38
|
+
puts "Worker forked!"
|
39
|
+
end
|
data/lib/thin.rb
CHANGED
@@ -1,45 +1,3 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'timeout'
|
3
|
-
require 'stringio'
|
4
|
-
require 'time'
|
5
|
-
require 'forwardable'
|
6
|
-
require 'openssl'
|
7
|
-
require 'eventmachine'
|
8
|
-
require 'rack'
|
9
|
-
|
10
|
-
module Thin
|
11
|
-
autoload :Command, "thin/command"
|
12
|
-
autoload :Connection, "thin/connection"
|
13
|
-
autoload :Daemonizable, "thin/daemonizing"
|
14
|
-
autoload :Logging, "thin/logging"
|
15
|
-
autoload :Headers, "thin/headers"
|
16
|
-
autoload :Request, "thin/request"
|
17
|
-
autoload :Response, "thin/response"
|
18
|
-
autoload :Runner, "thin/runner"
|
19
|
-
autoload :Server, "thin/server"
|
20
|
-
autoload :Stats, "thin/stats"
|
21
|
-
|
22
|
-
module Backends
|
23
|
-
autoload :Base, "thin/backends/base"
|
24
|
-
autoload :SwiftiplyClient, "thin/backends/swiftiply_client"
|
25
|
-
autoload :TcpServer, "thin/backends/tcp_server"
|
26
|
-
autoload :UnixServer, "thin/backends/unix_server"
|
27
|
-
end
|
28
|
-
|
29
|
-
module Controllers
|
30
|
-
autoload :Cluster, "thin/controllers/cluster"
|
31
|
-
autoload :Controller, "thin/controllers/controller"
|
32
|
-
autoload :Service, "thin/controllers/service"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
1
|
require "thin/version"
|
37
|
-
require "thin/
|
38
|
-
require "
|
39
|
-
require "thin_parser"
|
40
|
-
|
41
|
-
module Rack
|
42
|
-
module Adapter
|
43
|
-
autoload :Rails, "rack/adapter/rails"
|
44
|
-
end
|
45
|
-
end
|
2
|
+
require "thin/system"
|
3
|
+
require "thin/server"
|
data/lib/thin/async.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
module Thin
|
2
|
+
unless defined?(DeferrableBody)
|
3
|
+
# Based on version from James Tucker <raggi@rubyforge.org>
|
4
|
+
class DeferrableBody
|
5
|
+
include EM::Deferrable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@queue = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(body)
|
12
|
+
@queue << body
|
13
|
+
schedule_dequeue
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&blk)
|
17
|
+
@body_callback = blk
|
18
|
+
schedule_dequeue
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def schedule_dequeue
|
23
|
+
return unless @body_callback
|
24
|
+
EM.next_tick do
|
25
|
+
next unless body = @queue.shift
|
26
|
+
body.each do |chunk|
|
27
|
+
@body_callback.call(chunk)
|
28
|
+
end
|
29
|
+
schedule_dequeue unless @queue.empty?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Response whos body is sent asynchronously.
|
36
|
+
#
|
37
|
+
# A nice wrapper around Thin's obscure async callback used to send response body asynchronously.
|
38
|
+
# Which means you can send the response in chunks while allowing Thin to process other requests.
|
39
|
+
#
|
40
|
+
# Crazy delicious with em-http-request for file upload, image processing, proxying, etc.
|
41
|
+
#
|
42
|
+
# == _WARNING_
|
43
|
+
# You should not use long blocking operations (Net::HTTP or slow shell calls) with this as it
|
44
|
+
# will prevent the EventMachine event loop from running and block all other requests.
|
45
|
+
#
|
46
|
+
# Also disable the Rack::Lint middleware to use Thin's async feature since it requires sending
|
47
|
+
# back an invalid status code to the server.
|
48
|
+
#
|
49
|
+
# == Usage
|
50
|
+
# Inside your Rack app #call(env):
|
51
|
+
#
|
52
|
+
# response = Thin::AsyncResponse.new(env)
|
53
|
+
# response.status = 201
|
54
|
+
# response.headers["X-Muffin-Mode"] = "ACTIVATED!"
|
55
|
+
#
|
56
|
+
# response << "this is ... "
|
57
|
+
#
|
58
|
+
# EM.add_timer(1) do
|
59
|
+
# # This will be sent to the client 1 sec later without blocking other requests.
|
60
|
+
# response << "async!"
|
61
|
+
# response.done
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# response.finish
|
65
|
+
#
|
66
|
+
class AsyncResponse
|
67
|
+
include Rack::Response::Helpers
|
68
|
+
|
69
|
+
Marker = [-1, {}, []].freeze
|
70
|
+
|
71
|
+
attr_reader :headers, :callback
|
72
|
+
attr_accessor :status
|
73
|
+
|
74
|
+
def initialize(env, status=200, headers={})
|
75
|
+
@callback = env['async.callback']
|
76
|
+
@body = DeferrableBody.new
|
77
|
+
@status = status
|
78
|
+
@headers = headers
|
79
|
+
@headers_sent = false
|
80
|
+
|
81
|
+
yield self if block_given?
|
82
|
+
end
|
83
|
+
|
84
|
+
def send_headers(response=nil)
|
85
|
+
return if @headers_sent
|
86
|
+
@callback.call response || [@status, @headers, @body]
|
87
|
+
@headers_sent = true
|
88
|
+
end
|
89
|
+
|
90
|
+
def write(body)
|
91
|
+
send_headers
|
92
|
+
@body.call(body.respond_to?(:each) ? body : [body])
|
93
|
+
end
|
94
|
+
alias :<< :write
|
95
|
+
|
96
|
+
# Tell Thin the response is complete and the connection can be closed.
|
97
|
+
def done(response=nil)
|
98
|
+
send_headers(response)
|
99
|
+
EM.next_tick { @body.succeed }
|
100
|
+
end
|
101
|
+
|
102
|
+
# Tell Thin the response is gonna be sent asynchronously.
|
103
|
+
# The status code of -1 is the magic trick here.
|
104
|
+
def finish
|
105
|
+
Marker
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "preforker"
|
2
|
+
|
3
|
+
module Thin
|
4
|
+
module Backends
|
5
|
+
class Prefork
|
6
|
+
def initialize(server)
|
7
|
+
@server = server
|
8
|
+
end
|
9
|
+
|
10
|
+
def start(daemonize)
|
11
|
+
@server.before_fork.call(@server) if @server.before_fork
|
12
|
+
|
13
|
+
@prefork = Preforker.new(
|
14
|
+
:app_name => @server.to_s,
|
15
|
+
:workers => @server.worker_processes,
|
16
|
+
:timeout => @server.timeout,
|
17
|
+
:pid_path => @server.pid_path,
|
18
|
+
:stderr_path => @server.log_path,
|
19
|
+
:stdout_path => @server.log_path,
|
20
|
+
:logger => Logger.new(@server.log_path || $stdout)
|
21
|
+
) do |master|
|
22
|
+
|
23
|
+
EM.run do
|
24
|
+
EM.add_periodic_timer(4) do
|
25
|
+
EM.stop_event_loop unless master.wants_me_alive?
|
26
|
+
end
|
27
|
+
|
28
|
+
@server.after_fork.call(@server, master) if @server.after_fork
|
29
|
+
|
30
|
+
yield
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if daemonize
|
35
|
+
@prefork.start
|
36
|
+
else
|
37
|
+
@prefork.run
|
38
|
+
end
|
39
|
+
|
40
|
+
at_exit { @server.stop }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Thin
|
2
|
+
module Backends
|
3
|
+
class SingleProcess
|
4
|
+
def initialize(server)
|
5
|
+
@server = server
|
6
|
+
end
|
7
|
+
|
8
|
+
def start(daemonize)
|
9
|
+
raise NotImplementedError, "Daemonization not supported in single process mode" if daemonize
|
10
|
+
|
11
|
+
@pid_manager = Preforker::PidManager.new(@server.pid_path)
|
12
|
+
|
13
|
+
$0 = @server.to_s
|
14
|
+
|
15
|
+
# Install signals
|
16
|
+
%w( INT TERM ).each { |signal| trap(signal, "EXIT") }
|
17
|
+
at_exit do
|
18
|
+
@server.stop
|
19
|
+
@pid_manager.unlink
|
20
|
+
end
|
21
|
+
|
22
|
+
EM.run do
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Thin
|
2
|
+
# Same as Rack::Chunked::Body, but doesn't send the tail automaticaly.
|
3
|
+
class ChunkedBody
|
4
|
+
TERM = "\r\n"
|
5
|
+
TAIL = "0#{TERM}#{TERM}"
|
6
|
+
|
7
|
+
include Rack::Utils
|
8
|
+
|
9
|
+
def initialize(body)
|
10
|
+
@body = body
|
11
|
+
end
|
12
|
+
|
13
|
+
def each
|
14
|
+
term = TERM
|
15
|
+
@body.each do |chunk|
|
16
|
+
size = bytesize(chunk)
|
17
|
+
next if size == 0
|
18
|
+
|
19
|
+
chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
|
20
|
+
yield [size.to_s(16), term, chunk, term].join
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
@body.close if @body.respond_to?(:close)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
module Thin
|
2
|
+
class Configurator
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(defaults={}, &block)
|
6
|
+
@options = defaults.dup
|
7
|
+
@options[:listeners] ||= []
|
8
|
+
|
9
|
+
instance_eval(&block) if block
|
10
|
+
end
|
11
|
+
|
12
|
+
# {include:Thin::Server#worker_processes}
|
13
|
+
# @see Thin::Server#worker_processes
|
14
|
+
def worker_processes(number)
|
15
|
+
set :worker_processes, number, Integer
|
16
|
+
end
|
17
|
+
|
18
|
+
# {include:Thin::Server#worker_connections}
|
19
|
+
# @see Thin::Server#worker_connections
|
20
|
+
def worker_connections(number)
|
21
|
+
set :worker_connections, number, Integer
|
22
|
+
end
|
23
|
+
|
24
|
+
# {include:Thin::Server#listen}
|
25
|
+
# @see Thin::Server#listen
|
26
|
+
def listen(address, options={})
|
27
|
+
@options[:listeners] << Listener.new(address, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# {include:Thin::Server#preload_app}
|
31
|
+
# @see Thin::Server#preload_app
|
32
|
+
def preload_app(value)
|
33
|
+
set :preload_app, value, TrueClass, FalseClass
|
34
|
+
end
|
35
|
+
|
36
|
+
# {include:Thin::Server#timeout}
|
37
|
+
# @see Thin::Server#timeout
|
38
|
+
def timeout(seconds)
|
39
|
+
set :timeout, seconds, Integer
|
40
|
+
end
|
41
|
+
|
42
|
+
# {include:Thin::Server#timeout}
|
43
|
+
# @see Thin::Server#timeout
|
44
|
+
def keep_alive_requests(number)
|
45
|
+
set :max_keep_alive_requests, number, Integer
|
46
|
+
end
|
47
|
+
|
48
|
+
# {include:Thin::Server#log_path}
|
49
|
+
# @see Thin::Server#log_path
|
50
|
+
def log_path(path)
|
51
|
+
set :log_path, path, String
|
52
|
+
end
|
53
|
+
|
54
|
+
# {include:Thin::Server#pid_path}
|
55
|
+
# @see Thin::Server#pid_path
|
56
|
+
def pid_path(path)
|
57
|
+
set :pid_path, path, String
|
58
|
+
end
|
59
|
+
|
60
|
+
# {include:Thin::Server#use_epoll}
|
61
|
+
# @see Thin::Server#use_epoll
|
62
|
+
def use_epoll(value)
|
63
|
+
set :use_epoll, value, TrueClass, FalseClass
|
64
|
+
end
|
65
|
+
|
66
|
+
# {include:Thin::Server#use_kqueue}
|
67
|
+
# @see Thin::Server#use_kqueue
|
68
|
+
def use_kqueue(value)
|
69
|
+
set :use_kqueue, value, TrueClass, FalseClass
|
70
|
+
end
|
71
|
+
|
72
|
+
# {include:Thin::Server#threaded}
|
73
|
+
# @see Thin::Server#threaded
|
74
|
+
def threaded(value)
|
75
|
+
set :threaded, value, TrueClass, FalseClass
|
76
|
+
end
|
77
|
+
|
78
|
+
# {include:Thin::Server#thread_pool_size}
|
79
|
+
# @see Thin::Server#thread_pool_size
|
80
|
+
def thread_pool_size(value)
|
81
|
+
set :thread_pool_size, value, Integer
|
82
|
+
end
|
83
|
+
|
84
|
+
# {include:Thin::Server#before_fork}
|
85
|
+
# @see Thin::Server#before_fork
|
86
|
+
def before_fork(&block)
|
87
|
+
@options[:before_fork] = block
|
88
|
+
end
|
89
|
+
|
90
|
+
# {include:Thin::Server#after_fork}
|
91
|
+
# @see Thin::Server#after_fork
|
92
|
+
def after_fork(&block)
|
93
|
+
@options[:after_fork] = block
|
94
|
+
end
|
95
|
+
|
96
|
+
# Apply this configuration to the +server+ instance.
|
97
|
+
# @param [Thin::Server] server
|
98
|
+
def apply(server)
|
99
|
+
@options.each_pair { |name, value| server.send "#{name}=", value }
|
100
|
+
server
|
101
|
+
end
|
102
|
+
|
103
|
+
# Read and eval a configuration file and returns the resulting Configurator instance.
|
104
|
+
def self.load(file)
|
105
|
+
config = new
|
106
|
+
config.instance_eval(File.read(file), file)
|
107
|
+
config
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def set(name, value, *classes)
|
112
|
+
if classes.any? && classes.none? { |c| value.is_a?(c) }
|
113
|
+
raise ArgumentError, "#{name}: #{value.inspect} is not of type " + classes.join(' or ')
|
114
|
+
end
|
115
|
+
@options[name] = value
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|