thin 0.7.0-x86-mswin32-60 → 0.7.1-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 +11 -1
- data/COMMITTERS +3 -0
- data/README +4 -0
- data/Rakefile +2 -0
- data/ext/thin_parser/thin.c +9 -0
- data/lib/thin.rb +5 -5
- data/lib/thin/backends/base.rb +114 -0
- data/lib/thin/{connectors → backends}/swiftiply_client.rb +6 -6
- data/lib/thin/{connectors → backends}/tcp_server.rb +2 -2
- data/lib/thin/{connectors → backends}/unix_server.rb +19 -8
- data/lib/thin/connection.rb +3 -3
- data/lib/thin/controllers/controller.rb +6 -5
- data/lib/thin/controllers/service.rb +3 -1
- data/lib/thin/daemonizing.rb +20 -7
- data/lib/thin/logging.rb +1 -1
- data/lib/thin/request.rb +5 -5
- data/lib/thin/server.rb +63 -78
- data/lib/thin/version.rb +4 -2
- data/lib/thin_parser.so +0 -0
- data/spec/{connectors → backends}/swiftiply_client_spec.rb +10 -10
- data/spec/{connectors → backends}/tcp_server_spec.rb +5 -5
- data/spec/backends/unix_server_spec.rb +37 -0
- data/spec/command_spec.rb +1 -0
- data/spec/connection_spec.rb +1 -1
- data/spec/controllers/controller_spec.rb +1 -0
- data/spec/daemonizing_spec.rb +31 -23
- data/spec/{request/perf_spec.rb → perf/request_perf_spec.rb} +0 -0
- data/spec/perf/response_perf_spec.rb +19 -0
- data/spec/perf/server_perf_spec.rb +39 -0
- data/spec/request/mongrel_spec.rb +1 -1
- data/spec/response_spec.rb +0 -11
- data/spec/runner_spec.rb +4 -4
- data/spec/server/builder_spec.rb +8 -3
- data/spec/server/pipelining_spec.rb +6 -5
- data/spec/server/swiftiply_spec.rb +25 -20
- data/spec/server/tcp_spec.rb +1 -9
- data/spec/server/unix_socket_spec.rb +1 -5
- data/spec/server_spec.rb +4 -7
- data/spec/spec_helper.rb +29 -5
- data/tasks/announce.rake +2 -2
- data/tasks/email.erb +13 -15
- data/tasks/ext.rake +28 -24
- data/tasks/gem.rake +8 -4
- data/tasks/spec.rake +14 -9
- metadata +19 -15
- data/lib/thin/connectors/connector.rb +0 -73
- data/spec/connectors/unix_server_spec.rb +0 -43
data/CHANGELOG
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 0.7.1 Fancy Pants release
|
2
|
+
* Clean stale PID files when starting as daemon, fixes #53 [Chu Yeow]
|
3
|
+
* Require EventMachine 0.11.0 for UNIX domain sockets. Until it's released, install from:
|
4
|
+
gem install eventmachine --source http://code.macournoyer.com
|
5
|
+
* Ruby 1.8.5 compatibility, closes #49 [Wincent Colaiuta]
|
6
|
+
* Move all EventMachine stuff out of Server, you can now create a Thin Backend that
|
7
|
+
does not depend on EventMachine.
|
8
|
+
* Rename Connector to Backend. Extend Thin::Backends::Base to implement your own.
|
9
|
+
* Fix high memory usage with big POST body, fixes #48
|
10
|
+
|
1
11
|
== 0.7.0 Spherical Cow release
|
2
12
|
* Add --max-persistent-conns option to sets the maximum number of persistent connections.
|
3
13
|
Set to 0 to disable Keep-Alive.
|
@@ -13,7 +23,7 @@
|
|
13
23
|
* --debug (-D) option no longer set $DEBUG so logging will be less verbose
|
14
24
|
and Ruby won't be too strict, fixes #36.
|
15
25
|
* Deprecate Server#silent in favour of Logging.silent.
|
16
|
-
* Persistent connection (keep-alive)
|
26
|
+
* Persistent connection (keep-alive) support.
|
17
27
|
* Fix -s option not being included in generated config file, fixes #37.
|
18
28
|
* Add Swiftiply support. Use w/ the --swiftiply (-y) option in the thin script,
|
19
29
|
closes #28 [Alex MacCaw]
|
data/COMMITTERS
ADDED
data/README
CHANGED
@@ -10,6 +10,10 @@ Which makes it, with all humility, the most secure, stable, fast and extensible
|
|
10
10
|
bundled in an easy to use gem for your own pleasure.
|
11
11
|
|
12
12
|
=== Installation
|
13
|
+
IMPORTANT: Until EventMachine 0.11.0 is out, you have to install it from trunk or from my gem server:
|
14
|
+
|
15
|
+
sudo gem install eventmachine --source http://code.macournoyer.com
|
16
|
+
|
13
17
|
For the latest stable version:
|
14
18
|
|
15
19
|
sudo gem install thin
|
data/Rakefile
CHANGED
data/ext/thin_parser/thin.c
CHANGED
@@ -52,6 +52,15 @@ static VALUE global_path_info;
|
|
52
52
|
/** Defines global strings in the init method. */
|
53
53
|
#define DEF_GLOBAL(N, val) global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)
|
54
54
|
|
55
|
+
/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
|
56
|
+
#ifndef RSTRING_PTR
|
57
|
+
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
58
|
+
#endif
|
59
|
+
|
60
|
+
/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
|
61
|
+
#ifndef RSTRING_LEN
|
62
|
+
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
63
|
+
#endif
|
55
64
|
|
56
65
|
/* Defines the maximum allowed lengths for various input elements.*/
|
57
66
|
DEF_MAX_LENGTH(FIELD_NAME, 256);
|
data/lib/thin.rb
CHANGED
@@ -22,11 +22,11 @@ module Thin
|
|
22
22
|
autoload :Server, 'thin/server'
|
23
23
|
autoload :Stats, 'thin/stats'
|
24
24
|
|
25
|
-
module
|
26
|
-
autoload :
|
27
|
-
autoload :SwiftiplyClient, 'thin/
|
28
|
-
autoload :TcpServer, 'thin/
|
29
|
-
autoload :UnixServer, 'thin/
|
25
|
+
module Backends
|
26
|
+
autoload :Base, 'thin/backends/base'
|
27
|
+
autoload :SwiftiplyClient, 'thin/backends/swiftiply_client'
|
28
|
+
autoload :TcpServer, 'thin/backends/tcp_server'
|
29
|
+
autoload :UnixServer, 'thin/backends/unix_server'
|
30
30
|
end
|
31
31
|
|
32
32
|
module Controllers
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Thin
|
2
|
+
module Backends
|
3
|
+
# A Backend connects the server to the client. It handles:
|
4
|
+
# * connection/disconnection to the server
|
5
|
+
# * initialization of the connections
|
6
|
+
# * manitoring of the active connections.
|
7
|
+
class Base
|
8
|
+
# Server serving the connections throught the backend
|
9
|
+
attr_accessor :server
|
10
|
+
|
11
|
+
# Maximum time for incoming data to arrive
|
12
|
+
attr_accessor :timeout
|
13
|
+
|
14
|
+
# Maximum number of file or socket descriptors that the server may open.
|
15
|
+
attr_accessor :maximum_connections
|
16
|
+
|
17
|
+
# Maximum number of connections that can be persistent
|
18
|
+
attr_accessor :maximum_persistent_connections
|
19
|
+
|
20
|
+
# Number of persistent connections currently opened
|
21
|
+
attr_accessor :persistent_connection_count
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@connections = []
|
25
|
+
@timeout = Server::DEFAULT_TIMEOUT
|
26
|
+
@persistent_connection_count = 0
|
27
|
+
@maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS
|
28
|
+
@maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
@stopping = false
|
33
|
+
|
34
|
+
EventMachine.run do
|
35
|
+
connect
|
36
|
+
@running = true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@running = false
|
42
|
+
@stopping = true
|
43
|
+
|
44
|
+
# Do not accept anymore connection
|
45
|
+
disconnect
|
46
|
+
stop! if @connections.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop!
|
50
|
+
@running = false
|
51
|
+
@stopping = false
|
52
|
+
|
53
|
+
EventMachine.stop if EventMachine.reactor_running?
|
54
|
+
@connections.each { |connection| connection.close_connection }
|
55
|
+
close
|
56
|
+
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
# See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
|
60
|
+
EventMachine.epoll
|
61
|
+
|
62
|
+
# Set the maximum number of socket descriptors that the server may open.
|
63
|
+
# The process needs to have required privilege to set it higher the 1024 on
|
64
|
+
# some systems.
|
65
|
+
@maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
|
66
|
+
end
|
67
|
+
|
68
|
+
# Free up resources used by the backend.
|
69
|
+
def close
|
70
|
+
end
|
71
|
+
|
72
|
+
def running?
|
73
|
+
@running
|
74
|
+
end
|
75
|
+
|
76
|
+
# Called by a connection when it's unbinded.
|
77
|
+
def connection_finished(connection)
|
78
|
+
@persistent_connection_count -= 1 if connection.can_persist?
|
79
|
+
@connections.delete(connection)
|
80
|
+
|
81
|
+
# Finalize gracefull stop if there's no more active connection.
|
82
|
+
stop! if @stopping && @connections.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns +true+ if no active connection.
|
86
|
+
def empty?
|
87
|
+
@connections.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
# Number of active connections.
|
91
|
+
def size
|
92
|
+
@connections.size
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
# Initialize a new connection to a client.
|
97
|
+
def initialize_connection(connection)
|
98
|
+
connection.backend = self
|
99
|
+
connection.app = @server.app
|
100
|
+
connection.comm_inactivity_timeout = @timeout
|
101
|
+
|
102
|
+
# We control the number of persistent connections by keeping
|
103
|
+
# a count of the total one allowed yet.
|
104
|
+
if @persistent_connection_count < @maximum_persistent_connections
|
105
|
+
connection.can_persist!
|
106
|
+
@persistent_connection_count += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
@connections << connection
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Thin
|
2
|
-
module
|
3
|
-
class SwiftiplyClient <
|
2
|
+
module Backends
|
3
|
+
class SwiftiplyClient < Base
|
4
4
|
attr_accessor :key
|
5
5
|
|
6
6
|
attr_accessor :host, :port
|
@@ -30,7 +30,7 @@ module Thin
|
|
30
30
|
|
31
31
|
class SwiftiplyConnection < Connection
|
32
32
|
def connection_completed
|
33
|
-
send_data swiftiply_handshake(@
|
33
|
+
send_data swiftiply_handshake(@backend.key)
|
34
34
|
end
|
35
35
|
|
36
36
|
def persistent?
|
@@ -39,17 +39,17 @@ module Thin
|
|
39
39
|
|
40
40
|
def unbind
|
41
41
|
super
|
42
|
-
EventMachine.add_timer(rand(2)) { reconnect(@
|
42
|
+
EventMachine.add_timer(rand(2)) { reconnect(@backend.host, @backend.port) } if @backend.running?
|
43
43
|
end
|
44
44
|
|
45
45
|
protected
|
46
46
|
def swiftiply_handshake(key)
|
47
|
-
'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @
|
47
|
+
'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i)}.join << sprintf('%04x', @backend.port) << sprintf('%02x', key.length) << key
|
48
48
|
end
|
49
49
|
|
50
50
|
# For some reason Swiftiply request the current host
|
51
51
|
def host_ip
|
52
|
-
Socket.gethostbyname(@
|
52
|
+
Socket.gethostbyname(@backend.host)[3].unpack('CCCC') rescue [0,0,0,0]
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module Thin
|
2
|
-
module
|
3
|
-
#
|
4
|
-
class UnixServer <
|
2
|
+
module Backends
|
3
|
+
# Backend to act as a UNIX domain socket server.
|
4
|
+
class UnixServer < Base
|
5
5
|
# UNIX domain socket on which the server is listening for connections.
|
6
6
|
attr_accessor :socket
|
7
7
|
|
8
8
|
def initialize(socket)
|
9
|
-
raise PlatformNotSupported, 'UNIX sockets not available on Windows' if Thin.win?
|
9
|
+
raise PlatformNotSupported, 'UNIX domain sockets not available on Windows' if Thin.win?
|
10
|
+
check_event_machine_version
|
10
11
|
@socket = socket
|
11
12
|
super()
|
12
13
|
end
|
@@ -25,7 +26,7 @@ module Thin
|
|
25
26
|
EventMachine.stop_server(@signature)
|
26
27
|
end
|
27
28
|
|
28
|
-
# Free up resources used by the
|
29
|
+
# Free up resources used by the backend.
|
29
30
|
def close
|
30
31
|
remove_socket_file
|
31
32
|
end
|
@@ -38,15 +39,25 @@ module Thin
|
|
38
39
|
def remove_socket_file
|
39
40
|
File.delete(@socket) if @socket && File.exist?(@socket)
|
40
41
|
end
|
42
|
+
|
43
|
+
def check_event_machine_version
|
44
|
+
# TODO remove this crap once eventmachine 0.11.0 is released
|
45
|
+
begin
|
46
|
+
gem 'eventmachine', '>= 0.11.0'
|
47
|
+
rescue Gem::LoadError
|
48
|
+
raise LoadError, "UNIX domain sockets require EventMachine version 0.11.0 or higher, " +
|
49
|
+
"install the (not yet released) gem with: " +
|
50
|
+
"gem install eventmachine --source http://code.macournoyer.com"
|
51
|
+
end
|
52
|
+
end
|
41
53
|
end
|
42
54
|
end
|
43
55
|
|
44
56
|
# Connection through a UNIX domain socket.
|
45
57
|
class UnixConnection < Connection
|
46
58
|
protected
|
47
|
-
def socket_address
|
48
|
-
#
|
49
|
-
Socket.unpack_sockaddr_un(get_peername)
|
59
|
+
def socket_address
|
60
|
+
'127.0.0.1' # Unix domain sockets can only be local
|
50
61
|
end
|
51
62
|
end
|
52
63
|
end
|
data/lib/thin/connection.rb
CHANGED
@@ -10,8 +10,8 @@ module Thin
|
|
10
10
|
# Rack application served by this connection.
|
11
11
|
attr_accessor :app
|
12
12
|
|
13
|
-
#
|
14
|
-
attr_accessor :
|
13
|
+
# Backend to the server
|
14
|
+
attr_accessor :backend
|
15
15
|
|
16
16
|
# Current request served by the connection
|
17
17
|
attr_accessor :request
|
@@ -72,7 +72,7 @@ module Thin
|
|
72
72
|
# Called when the connection is unbinded from the socket
|
73
73
|
# and can no longer be used to process requests.
|
74
74
|
def unbind
|
75
|
-
@
|
75
|
+
@backend.connection_finished(self)
|
76
76
|
end
|
77
77
|
|
78
78
|
# Allows this connection to be persistent.
|
@@ -31,7 +31,7 @@ module Thin
|
|
31
31
|
when @options.has_key?(:socket)
|
32
32
|
Server.new(@options[:socket])
|
33
33
|
when @options.has_key?(:swiftiply)
|
34
|
-
Server.new(
|
34
|
+
Server.new(Backends::SwiftiplyClient.new(@options[:address], @options[:port], @options[:swiftiply]))
|
35
35
|
else
|
36
36
|
Server.new(@options[:address], @options[:port])
|
37
37
|
end
|
@@ -44,8 +44,8 @@ module Thin
|
|
44
44
|
|
45
45
|
server.daemonize if @options[:daemonize]
|
46
46
|
|
47
|
-
# Must be called before changing privileges since it might require superuser power.
|
48
|
-
|
47
|
+
server.config # Must be called before changing privileges since it might require superuser power.
|
48
|
+
|
49
49
|
server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group]
|
50
50
|
|
51
51
|
# If a Rack config file is specified we eval it inside a Rack::Builder block to create
|
@@ -120,10 +120,10 @@ module Thin
|
|
120
120
|
|
121
121
|
# Acts like GNU tail command. Taken from Rails.
|
122
122
|
def tail(file)
|
123
|
+
cursor = File.exist?(file) ? File.size(file) : 0
|
124
|
+
last_checked = Time.now
|
123
125
|
tail_thread = Thread.new do
|
124
126
|
Thread.pass until File.exist?(file)
|
125
|
-
cursor = File.size(file)
|
126
|
-
last_checked = Time.now
|
127
127
|
File.open(file, 'r') do |f|
|
128
128
|
loop do
|
129
129
|
f.seek cursor
|
@@ -138,6 +138,7 @@ module Thin
|
|
138
138
|
end
|
139
139
|
end
|
140
140
|
end
|
141
|
+
sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file
|
141
142
|
tail_thread
|
142
143
|
end
|
143
144
|
end
|
@@ -50,8 +50,10 @@ module Thin
|
|
50
50
|
log "To configure thin to start at system boot:"
|
51
51
|
log "on RedHat like systems:"
|
52
52
|
log " sudo /sbin/chkconfig --level 345 #{NAME} on"
|
53
|
-
log "on Debian
|
53
|
+
log "on Debian-like systems (Ubuntu):"
|
54
54
|
log " sudo /usr/sbin/update-rc.d -f #{NAME} defaults"
|
55
|
+
log "on Gentoo:"
|
56
|
+
log " sudo rc-update add #{NAME} default"
|
55
57
|
log ''
|
56
58
|
log "Then put your config files in #{config_files_path}"
|
57
59
|
end
|
data/lib/thin/daemonizing.rb
CHANGED
@@ -34,10 +34,10 @@ module Thin
|
|
34
34
|
|
35
35
|
# Turns the current script into a daemon process that detaches from the console.
|
36
36
|
def daemonize
|
37
|
-
raise PlatformNotSupported, 'Daemonizing not supported on Windows' if Thin.win?
|
37
|
+
raise PlatformNotSupported, 'Daemonizing is not supported on Windows' if Thin.win?
|
38
38
|
raise ArgumentError, 'You must specify a pid_file to daemonize' unless @pid_file
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
remove_stale_pid_file
|
41
41
|
|
42
42
|
pwd = Dir.pwd # Current directory is changed during daemonization, so store it
|
43
43
|
|
@@ -53,7 +53,7 @@ module Thin
|
|
53
53
|
remove_pid_file
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
# Change privileges of the process
|
58
58
|
# to the specified user and group.
|
59
59
|
def change_privilege(user, group=user)
|
@@ -73,12 +73,12 @@ module Thin
|
|
73
73
|
log "Couldn't change user and group to #{user}:#{group}: #{e}"
|
74
74
|
end
|
75
75
|
|
76
|
-
#
|
76
|
+
# Register a proc to be called to restart the server.
|
77
77
|
def on_restart(&block)
|
78
78
|
@on_restart = block
|
79
79
|
end
|
80
80
|
|
81
|
-
# Restart the server
|
81
|
+
# Restart the server.
|
82
82
|
def restart
|
83
83
|
raise ArgumentError, "Can't restart, no 'on_restart' proc specified" unless @on_restart
|
84
84
|
log '>> Restarting ...'
|
@@ -108,7 +108,7 @@ module Thin
|
|
108
108
|
File.delete(pid_file) if File.exist?(pid_file)
|
109
109
|
end
|
110
110
|
|
111
|
-
# Restart the server by sending HUP signal
|
111
|
+
# Restart the server by sending HUP signal.
|
112
112
|
def restart(pid_file)
|
113
113
|
send_signal('HUP', pid_file)
|
114
114
|
end
|
@@ -142,5 +142,18 @@ module Thin
|
|
142
142
|
open(@pid_file,"w") { |f| f.write(Process.pid) }
|
143
143
|
File.chmod(0644, @pid_file)
|
144
144
|
end
|
145
|
+
|
146
|
+
# If PID file is stale, remove it.
|
147
|
+
def remove_stale_pid_file
|
148
|
+
if File.exist?(@pid_file)
|
149
|
+
if pid && Process.running?(pid)
|
150
|
+
raise PidFileExist, "#{@pid_file} already exists, seems like it's already running (process ID: #{pid}). " +
|
151
|
+
"Stop the process or delete #{@pid_file}."
|
152
|
+
else
|
153
|
+
log ">> Deleting stale PID file #{@pid_file}"
|
154
|
+
remove_pid_file
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
145
158
|
end
|
146
159
|
end
|
data/lib/thin/logging.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Thin
|
2
2
|
# To be included in classes to allow some basic logging
|
3
|
-
# that can be
|
3
|
+
# that can be silenced (<tt>Logging.silent=</tt>) or made
|
4
4
|
# more verbose.
|
5
5
|
# <tt>Logging.debug=</tt>: log all error backtrace and messages
|
6
6
|
# logged with +debug+.
|
data/lib/thin/request.rb
CHANGED
@@ -52,7 +52,7 @@ module Thin
|
|
52
52
|
# Rack stuff
|
53
53
|
RACK_INPUT => @body,
|
54
54
|
|
55
|
-
RACK_VERSION =>
|
55
|
+
RACK_VERSION => VERSION::RACK,
|
56
56
|
RACK_ERRORS => STDERR,
|
57
57
|
|
58
58
|
RACK_MULTITHREAD => false,
|
@@ -65,11 +65,10 @@ module Thin
|
|
65
65
|
# Raises a +InvalidRequest+ if invalid.
|
66
66
|
# Returns +true+ if the parsing is complete.
|
67
67
|
def parse(data)
|
68
|
-
@data << data
|
69
|
-
|
70
68
|
if @parser.finished? # Header finished, can only be some more body
|
71
69
|
body << data
|
72
70
|
else # Parse more header using the super parser
|
71
|
+
@data << data
|
73
72
|
@nparsed = @parser.execute(@env, @data, @nparsed)
|
74
73
|
|
75
74
|
# Transfert to a tempfile if body is very big
|
@@ -78,9 +77,10 @@ module Thin
|
|
78
77
|
|
79
78
|
|
80
79
|
if finished? # Check if header and body are complete
|
81
|
-
@
|
80
|
+
@data = nil
|
81
|
+
@body.rewind
|
82
82
|
true # Request is fully parsed
|
83
|
-
else
|
83
|
+
else
|
84
84
|
false # Not finished, need more data
|
85
85
|
end
|
86
86
|
end
|