Skip to content

Commit

Permalink
Merge branch 'master' of git://github.com/macournoyer/thin
Browse files Browse the repository at this point in the history
  • Loading branch information
sikachu committed Aug 2, 2009
2 parents 1d3f743 + dacb5df commit d908862
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 60 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
== 1.2.3
* Fix uninitialized constant ActionController::Dispatcher error with Rails 1.2.3 [Chris Anderton] [#103 state:resolved]

== 1.2.2 I Find Your Lack of Sauce Disturbing release
* Fix force kill under 1.9 [Alexey Chebotar]
* Fix regression when --only option is used w/ --socket.
* Add process name 'tag' functionality. Easier to distinguish thin daemons
from eachother in process listing [ctcherry]

== 1.2.1 Asynctilicious Ultra Supreme release
* Require Rack 1.0.0
* Require EventMachine 0.12.6
* Use Rails Rack based dispatcher when available
* Allow String for response body
* Require openssl before eventmachine to prevent crash in 1.9

== 1.2.0 Asynctilicious Supreme release
* Add support for Windows mingw Ruby distro [Juan C. Rodriguez]
* Add async response support, see example/async_*.ru [raggi]
Expand Down
8 changes: 0 additions & 8 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,12 @@ For the latest stable version:

sudo gem install thin

or using my mirror (might be more recent and unstable):

sudo gem install thin --source http://code.macournoyer.com

Or from source:

git clone git://github.com/macournoyer/thin.git
cd thin
rake install

To use Thin with UNIX domain sockets you need EventMachine 0.11.0 from my gem server:

gem install eventmachine --source http://code.macournoyer.com

=== Usage
A +thin+ script offers an easy way to start your Rails application:

Expand Down
2 changes: 1 addition & 1 deletion example/config.ru
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# Check Rack::Builder doc for more details on this file format:
# http://rack.rubyforge.org/doc/classes/Rack/Builder.html

require File.dirname(__FILE__) + '/../lib/thin'
require ::File.dirname(__FILE__) + '/../lib/thin'

app = proc do |env|
# Response body has to respond to each and yield strings
Expand Down
48 changes: 25 additions & 23 deletions lib/rack/adapter/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ def initialize(options={})

load_application

@file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
@rails_app = if ActionController.const_defined?(:Dispatcher) && ActionController::Dispatcher.instance_methods.include?(:call)
ActionController::Dispatcher.new
else
CgiApp.new
end

@file_app = Rack::File.new(::File.join(RAILS_ROOT, "public"))
end

def load_application
Expand All @@ -32,55 +38,51 @@ def load_application
require 'dispatcher'

if @prefix
if ActionController::Base.respond_to?('relative_url_root=')
if ActionController::Base.respond_to?(:relative_url_root=)
ActionController::Base.relative_url_root = @prefix # Rails 2.1.1
else
ActionController::AbstractRequest.relative_url_root = @prefix
end
end
end

# TODO refactor this in File#can_serve?(path) ??
def file_exist?(path)
full_path = ::File.join(@file_server.root, Utils.unescape(path))
full_path = ::File.join(@file_app.root, Utils.unescape(path))
::File.file?(full_path) && ::File.readable_real?(full_path)
end

def serve_file(env)
@file_server.call(env)
end

def serve_rails(env)
request = Request.new(env)
response = Response.new

session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
cgi = CGIWrapper.new(request, response)

Dispatcher.dispatch(cgi, session_options, response)

response.finish
end

def call(env)
path = env['PATH_INFO'].chomp('/')
method = env['REQUEST_METHOD']
cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension

if FILE_METHODS.include?(method)
if file_exist?(path) # Serve the file if it's there
return serve_file(env)
return @file_app.call(env)
elsif file_exist?(cached_path) # Serve the page cache if it's there
env['PATH_INFO'] = cached_path
return serve_file(env)
return @file_app.call(env)
end
end

# No static file, let Rails handle it
serve_rails(env)
@rails_app.call(env)
end

protected
# For Rails pre Rack (2.3)
class CgiApp
def call(env)
request = Request.new(env)
response = Response.new
session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
cgi = CGIWrapper.new(request, response)

Dispatcher.dispatch(cgi, session_options, response)

response.finish
end
end

class CGIWrapper < ::CGI
def initialize(request, response, *args)
Expand Down
1 change: 1 addition & 0 deletions lib/thin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require 'forwardable'

require 'rubygems'
require 'openssl'
require 'eventmachine'

require 'thin/version'
Expand Down
2 changes: 1 addition & 1 deletion lib/thin/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def handle_error
end

def close_request_response
@request.async_close.succeed
@request.async_close.succeed if @request.async_close
@request.close rescue nil
@response.close rescue nil
end
Expand Down
4 changes: 2 additions & 2 deletions lib/thin/controllers/cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ def run(cmd, number)

def with_each_server
if only
if only < 80
if first_port && only < 80
# interpret +only+ as a sequence number
yield(first_port + only)
yield first_port + only
else
# interpret +only+ as an absolute port number
yield only
Expand Down
23 changes: 17 additions & 6 deletions lib/thin/daemonizing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,14 @@ def restart(pid_file)

# Send a +signal+ to the process which PID is stored in +pid_file+.
def send_signal(signal, pid_file, timeout=60)
if File.file?(pid_file) && pid = File.read(pid_file)
pid = pid.to_i
if pid = read_pid_file(pid_file)
Logging.log "Sending #{signal} signal to process #{pid} ... "
Process.kill(signal, pid)
Timeout.timeout(timeout) do
sleep 0.1 while Process.running?(pid)
end
Logging.log ""
else
puts "Can't stop process, no PID found in #{pid_file}"
Logging.log "Can't stop process, no PID found in #{pid_file}"
end
rescue Timeout::Error
Logging.log "Timeout!"
Expand All @@ -130,8 +128,21 @@ def send_signal(signal, pid_file, timeout=60)
end

def force_kill(pid_file)
Process.kill("KILL", File.read(pid_file)) rescue nil
File.delete(pid_file) if File.exist?(pid_file) rescue nil
if pid = read_pid_file(pid_file)
Logging.log "Sending KILL signal to process #{pid} ... "
Process.kill("KILL", pid)
File.delete(pid_file) if File.exist?(pid_file)
else
Logging.log "Can't stop process, no PID found in #{pid_file}"
end
end

def read_pid_file(file)
if File.file?(file) && pid = File.read(file)
pid.to_i
else
nil
end
end
end

Expand Down
6 changes: 4 additions & 2 deletions lib/thin/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ def close
# define your own +each+ method on +body+.
def each
yield head
@body.each do |chunk|
yield chunk
if @body.is_a?(String)
yield @body
else
@body.each { |chunk| yield chunk }
end
end

Expand Down
1 change: 1 addition & 0 deletions lib/thin/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def parser
"(default: #{@options[:pid]})") { |file| @options[:pid] = file }
opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| @options[:user] = user }
opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| @options[:group] = group }
opts.on( "--tag NAME", "Additional text to display in process listing") { |tag| @options[:tag] = tag }

opts.separator ""
opts.separator "Cluster options:"
Expand Down
10 changes: 8 additions & 2 deletions lib/thin/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ class Server

# Application (Rack adapter) called with the request that produces the response.
attr_accessor :app


# A tag that will show in the process listing
attr_accessor :tag

# Backend handling the connections to the clients.
attr_accessor :backend

Expand Down Expand Up @@ -103,6 +106,9 @@ def initialize(*args, &block)
end
end

# Set tag if needed
self.tag = options[:tag]

# Try to intelligently select which backend to use.
@backend = select_backend(host, port, options)

Expand Down Expand Up @@ -188,7 +194,7 @@ def config
# Name of the server and type of backend used.
# This is also the name of the process in which Thin is running as a daemon.
def name
"thin server (#{@backend})"
"thin server (#{@backend})" + (tag ? " [#{tag}]" : "")
end
alias :to_s :name

Expand Down
6 changes: 3 additions & 3 deletions lib/thin/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ class PlatformNotSupported < RuntimeError; end
module VERSION #:nodoc:
MAJOR = 1
MINOR = 2
TINY = 0
TINY = 2

STRING = [MAJOR, MINOR, TINY].join('.')

CODENAME = "Asynctilicious Supreme".freeze
CODENAME = "I Find Your Lack of Sauce Disturbing".freeze

RACK = [0, 1].freeze # Rack protocol version that was tested
RACK = [1, 0].freeze # Rack protocol version
end

NAME = 'thin'.freeze
Expand Down
3 changes: 2 additions & 1 deletion site/thin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,11 @@ def users_page
ul do
li { a 'Heroku', :href => 'http://heroku.com/' }
li { a 'HostingRails', :href => 'http://www.hostingrails.com/mongrel_and_thin_hosting' }
li { a 'Mobile Defense', :href => 'https://www.mobiledefense.com' }
li { a 'RefactorMyCode.com', :href => 'http://refactormycode.com/' }
li { a "Standout Jobs", :href => 'http://standoutjobs.com/'}
li { a "Kevin Williams Blog", :href => 'http://www.almostserio.us/articles/tag/thin' }
li { a "Gitorious", :href => 'http://gitorious.org/' }
li { a "Kevin Williams Blog", :href => 'http://www.almostserio.us/articles/tag/thin' }
li { a "Dinooz", :href => 'http://www.nicomoayudarte.com/' }
li { a "Mobile Dyne Systems", :href => 'http://www.mobiledyne.com/' }
li { a "feelfree.homelinux.com", :href => 'http://feelfree.homelinux.com' }
Expand Down
23 changes: 23 additions & 0 deletions spec/controllers/cluster_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,29 @@ def options_for_port(port)
end
end

describe Cluster, "controlling only one server with UNIX socket" do
before do
@cluster = Cluster.new(:chdir => '/rails_app',
:socket => '/tmp/thin.sock',
:address => '0.0.0.0',
:port => 3000,
:servers => 3,
:timeout => 10,
:log => 'thin.log',
:pid => 'thin.pid',
:only => 1
)
end

it 'should call only specified server' do
calls = []
@cluster.send(:with_each_server) do |n|
calls << n
end
calls.should == [1]
end
end

describe Cluster, "controlling only one server, by sequence number" do
before do
@cluster = Cluster.new(:chdir => '/rails_app',
Expand Down
8 changes: 8 additions & 0 deletions spec/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@
end

it 'should output body' do
@response.body = ['<html>', '</html>']

out = ''
@response.each { |l| out << l }
out.should include("\r\n\r\n<html></html>")
end

it 'should output String body' do
@response.body = '<html></html>'

out = ''
Expand Down
16 changes: 13 additions & 3 deletions spec/server/stopping_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@
start_server do |env|
[200, { 'Content-Type' => 'text/html' }, ['ok']]
end
@done = false
end

it "should wait for current requests before soft stopping" do
socket = TCPSocket.new('0.0.0.0', 3333)
socket.write("GET / HTTP/1.1")
@server.stop # Stop the server in the middle of a request
socket.write("\r\n\r\n")
EventMachine.next_tick do
@server.stop # Stop the server in the middle of a request
socket.write("\r\n\r\n")
@done = true
end

timeout(2) do
Thread.pass until @done
end

out = socket.read
socket.close
Expand All @@ -36,7 +44,9 @@
socket.write("GET / HTTP/1.1")
@server.stop! # Force stop the server in the middle of a request

EventMachine.next_tick { socket.should be_closed }
EventMachine.next_tick do
socket.should be_closed
end
end

after do
Expand Down
4 changes: 2 additions & 2 deletions tasks/gem.rake
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ spec = Gem::Specification.new do |s|

s.required_ruby_version = '>= 1.8.5'

s.add_dependency 'rack', '>= 0.9.1'
s.add_dependency 'eventmachine', '>= 0.12.4'
s.add_dependency 'rack', '>= 1.0.0'
s.add_dependency 'eventmachine', '>= 0.12.6'
unless WIN
s.add_dependency 'daemons', '>= 1.0.9'
end
Expand Down
Loading

0 comments on commit d908862

Please sign in to comment.