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.

Files changed (117) hide show
  1. data/CHANGELOG +40 -0
  2. data/COPYING +18 -0
  3. data/README +60 -0
  4. data/Rakefile +11 -0
  5. data/benchmark/simple.rb +48 -0
  6. data/bin/thin +123 -0
  7. data/doc/benchmarks.txt +86 -0
  8. data/doc/rdoc/classes/Process.html +181 -0
  9. data/doc/rdoc/classes/Rack.html +156 -0
  10. data/doc/rdoc/classes/Rack/Adapter.html +155 -0
  11. data/doc/rdoc/classes/Rack/Adapter/Rails.html +289 -0
  12. data/doc/rdoc/classes/Rack/Adapter/Rails/CGIWrapper.html +359 -0
  13. data/doc/rdoc/classes/Rack/Handler.html +155 -0
  14. data/doc/rdoc/classes/Rack/Handler/Thin.html +175 -0
  15. data/doc/rdoc/classes/Thin.html +164 -0
  16. data/doc/rdoc/classes/Thin/Cluster.html +399 -0
  17. data/doc/rdoc/classes/Thin/Connection.html +223 -0
  18. data/doc/rdoc/classes/Thin/Daemonizable.html +260 -0
  19. data/doc/rdoc/classes/Thin/Daemonizable/ClassMethods.html +197 -0
  20. data/doc/rdoc/classes/Thin/Headers.html +238 -0
  21. data/doc/rdoc/classes/Thin/InvalidRequest.html +144 -0
  22. data/doc/rdoc/classes/Thin/Logging.html +201 -0
  23. data/doc/rdoc/classes/Thin/Request.html +231 -0
  24. data/doc/rdoc/classes/Thin/Response.html +271 -0
  25. data/doc/rdoc/classes/Thin/Server.html +295 -0
  26. data/doc/rdoc/classes/Thin/StopServer.html +143 -0
  27. data/doc/rdoc/created.rid +1 -0
  28. data/doc/rdoc/files/README.html +226 -0
  29. data/doc/rdoc/files/bin/thin.html +245 -0
  30. data/doc/rdoc/files/lib/rack/adapter/rails_rb.html +146 -0
  31. data/doc/rdoc/files/lib/rack/handler/thin_rb.html +146 -0
  32. data/doc/rdoc/files/lib/thin/cluster_rb.html +146 -0
  33. data/doc/rdoc/files/lib/thin/connection_rb.html +145 -0
  34. data/doc/rdoc/files/lib/thin/daemonizing_rb.html +146 -0
  35. data/doc/rdoc/files/lib/thin/headers_rb.html +146 -0
  36. data/doc/rdoc/files/lib/thin/logging_rb.html +146 -0
  37. data/doc/rdoc/files/lib/thin/request_rb.html +145 -0
  38. data/doc/rdoc/files/lib/thin/response_rb.html +145 -0
  39. data/doc/rdoc/files/lib/thin/server_rb.html +145 -0
  40. data/doc/rdoc/files/lib/thin/statuses_rb.html +145 -0
  41. data/doc/rdoc/files/lib/thin/version_rb.html +145 -0
  42. data/doc/rdoc/files/lib/thin_rb.html +152 -0
  43. data/doc/rdoc/index.html +10 -0
  44. data/doc/rdoc/logo.gif +0 -0
  45. data/doc/rdoc/rdoc-style.css +55 -0
  46. data/example/config.ru +9 -0
  47. data/example/thin.god +72 -0
  48. data/ext/thin_parser/common.rl +54 -0
  49. data/ext/thin_parser/ext_help.h +14 -0
  50. data/ext/thin_parser/extconf.rb +6 -0
  51. data/ext/thin_parser/parser.c +1199 -0
  52. data/ext/thin_parser/parser.h +49 -0
  53. data/ext/thin_parser/parser.rl +143 -0
  54. data/ext/thin_parser/thin.c +424 -0
  55. data/lib/rack/adapter/rails.rb +155 -0
  56. data/lib/rack/handler/thin.rb +13 -0
  57. data/lib/thin.rb +36 -0
  58. data/lib/thin/cluster.rb +106 -0
  59. data/lib/thin/connection.rb +46 -0
  60. data/lib/thin/daemonizing.rb +112 -0
  61. data/lib/thin/headers.rb +37 -0
  62. data/lib/thin/logging.rb +23 -0
  63. data/lib/thin/request.rb +72 -0
  64. data/lib/thin/response.rb +48 -0
  65. data/lib/thin/server.rb +80 -0
  66. data/lib/thin/statuses.rb +43 -0
  67. data/lib/thin/version.rb +11 -0
  68. data/lib/thin_parser.so +0 -0
  69. data/spec/cluster_spec.rb +58 -0
  70. data/spec/daemonizing_spec.rb +93 -0
  71. data/spec/headers_spec.rb +35 -0
  72. data/spec/rack_rails_spec.rb +92 -0
  73. data/spec/rails_app/app/controllers/application.rb +10 -0
  74. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  75. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  76. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  77. data/spec/rails_app/config/boot.rb +109 -0
  78. data/spec/rails_app/config/environment.rb +64 -0
  79. data/spec/rails_app/config/environments/development.rb +18 -0
  80. data/spec/rails_app/config/environments/production.rb +19 -0
  81. data/spec/rails_app/config/environments/test.rb +22 -0
  82. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  83. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  84. data/spec/rails_app/config/routes.rb +35 -0
  85. data/spec/rails_app/public/404.html +30 -0
  86. data/spec/rails_app/public/422.html +30 -0
  87. data/spec/rails_app/public/500.html +30 -0
  88. data/spec/rails_app/public/dispatch.cgi +10 -0
  89. data/spec/rails_app/public/dispatch.fcgi +24 -0
  90. data/spec/rails_app/public/dispatch.rb +10 -0
  91. data/spec/rails_app/public/favicon.ico +0 -0
  92. data/spec/rails_app/public/images/rails.png +0 -0
  93. data/spec/rails_app/public/index.html +277 -0
  94. data/spec/rails_app/public/javascripts/application.js +2 -0
  95. data/spec/rails_app/public/javascripts/controls.js +963 -0
  96. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  97. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  98. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  99. data/spec/rails_app/public/robots.txt +5 -0
  100. data/spec/rails_app/script/about +3 -0
  101. data/spec/rails_app/script/console +3 -0
  102. data/spec/rails_app/script/destroy +3 -0
  103. data/spec/rails_app/script/generate +3 -0
  104. data/spec/rails_app/script/performance/benchmarker +3 -0
  105. data/spec/rails_app/script/performance/profiler +3 -0
  106. data/spec/rails_app/script/performance/request +3 -0
  107. data/spec/rails_app/script/plugin +3 -0
  108. data/spec/rails_app/script/process/inspector +3 -0
  109. data/spec/rails_app/script/process/reaper +3 -0
  110. data/spec/rails_app/script/process/spawner +3 -0
  111. data/spec/rails_app/script/runner +3 -0
  112. data/spec/rails_app/script/server +3 -0
  113. data/spec/request_spec.rb +258 -0
  114. data/spec/response_spec.rb +56 -0
  115. data/spec/server_spec.rb +75 -0
  116. data/spec/spec_helper.rb +127 -0
  117. metadata +219 -0
@@ -0,0 +1,155 @@
1
+ # This as been submitted to Rack as a patch, tested and everything.
2
+ # Bug Christian Neukirchen at chneukirchen@gmail.com to apply the patch!
3
+
4
+ require 'cgi'
5
+
6
+ # Adapter to run a Rails app with any supported Rack handler.
7
+ # By default it will try to load the Rails application in the
8
+ # current directory in the development environment.
9
+ # Options:
10
+ # root: Root directory of the Rails app
11
+ # env: Rails environment to run in (development, production or test)
12
+ # Based on http://fuzed.rubyforge.org/ Rails adapter
13
+ module Rack
14
+ module Adapter
15
+ class Rails
16
+ def initialize(options={})
17
+ @root = options[:root] || Dir.pwd
18
+ @env = options[:environment] || 'development'
19
+ @prefix = options[:prefix]
20
+
21
+ load_application
22
+
23
+ @file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
24
+ end
25
+
26
+ def load_application
27
+ ENV['RAILS_ENV'] = @env
28
+
29
+ require "#{@root}/config/environment"
30
+ require 'dispatcher'
31
+
32
+ ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
33
+ end
34
+
35
+ # TODO refactor this in File#can_serve?(path) ??
36
+ def file_exist?(path)
37
+ full_path = ::File.join(@file_server.root, Utils.unescape(path))
38
+ ::File.file?(full_path) && ::File.readable?(full_path)
39
+ end
40
+
41
+ def serve_file(env)
42
+ @file_server.call(env)
43
+ end
44
+
45
+ def serve_rails(env)
46
+ request = Request.new(env)
47
+ response = Response.new
48
+
49
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
50
+ cgi = CGIWrapper.new(request, response)
51
+
52
+ Dispatcher.dispatch(cgi, session_options, response)
53
+
54
+ response.finish
55
+ end
56
+
57
+ def call(env)
58
+ path = env['PATH_INFO'].chomp('/')
59
+ cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
60
+
61
+ if file_exist?(path) # Serve the file if it's there
62
+ serve_file(env)
63
+ elsif file_exist?(cached_path) # Serve the page cache if it's there
64
+ env['PATH_INFO'] = cached_path
65
+ serve_file(env)
66
+ else # No static file, let Rails handle it
67
+ serve_rails(env)
68
+ end
69
+ end
70
+
71
+ protected
72
+
73
+ class CGIWrapper < ::CGI
74
+ def initialize(request, response, *args)
75
+ @request = request
76
+ @response = response
77
+ @args = *args
78
+ @input = request.body
79
+
80
+ super *args
81
+ end
82
+
83
+ def header(options = "text/html")
84
+ if options.is_a?(String)
85
+ @response['Content-Type'] = options unless @response['Content-Type']
86
+ else
87
+ @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
88
+
89
+ @response['Content-Type'] = options.delete('type') || "text/html"
90
+ @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
91
+
92
+ @response['Content-Language'] = options.delete('language') if options['language']
93
+ @response['Expires'] = options.delete('expires') if options['expires']
94
+
95
+ @response.status = options.delete('Status') if options['Status']
96
+
97
+ # Convert 'cookie' header to 'Set-Cookie' headers.
98
+ # Because Set-Cookie header can appear more the once in the response body,
99
+ # we store it in a line break seperated string that will be translated to
100
+ # multiple Set-Cookie header by the handler.
101
+ if cookie = options.delete('cookie')
102
+ cookies = []
103
+
104
+ case cookie
105
+ when Array then cookie.each { |c| cookies << c.to_s }
106
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
107
+ else cookies << cookie.to_s
108
+ end
109
+
110
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
111
+
112
+ @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact.join("\n")
113
+ end
114
+
115
+ options.each { |k,v| @response[k] = v }
116
+ end
117
+
118
+ ""
119
+ end
120
+
121
+ def params
122
+ @params ||= @request.params
123
+ end
124
+
125
+ def cookies
126
+ @request.cookies
127
+ end
128
+
129
+ def query_string
130
+ @request.query_string
131
+ end
132
+
133
+ # Used to wrap the normal args variable used inside CGI.
134
+ def args
135
+ @args
136
+ end
137
+
138
+ # Used to wrap the normal env_table variable used inside CGI.
139
+ def env_table
140
+ @request.env
141
+ end
142
+
143
+ # Used to wrap the normal stdinput variable used inside CGI.
144
+ def stdinput
145
+ @input
146
+ end
147
+
148
+ def stdoutput
149
+ STDERR.puts "stdoutput should not be used."
150
+ @response.body
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,13 @@
1
+ module Rack
2
+ module Handler
3
+ class Thin
4
+ def self.run(app, options={})
5
+ server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
6
+ options[:Port] || 8080,
7
+ app)
8
+ yield server if block_given?
9
+ server.start!
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/thin.rb ADDED
@@ -0,0 +1,36 @@
1
+ $: << File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'fileutils'
4
+ require 'timeout'
5
+ require 'stringio'
6
+
7
+ require 'rubygems'
8
+ require 'eventmachine'
9
+
10
+ require 'thin/version'
11
+ require 'thin/statuses'
12
+
13
+ module Thin
14
+ NAME = 'thin'.freeze
15
+ SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze
16
+
17
+ autoload :Cluster, 'thin/cluster'
18
+ autoload :Connection, 'thin/connection'
19
+ autoload :Daemonizable, 'thin/daemonizing'
20
+ autoload :Logging, 'thin/logging'
21
+ autoload :Headers, 'thin/headers'
22
+ autoload :Request, 'thin/request'
23
+ autoload :Response, 'thin/response'
24
+ autoload :Server, 'thin/server'
25
+ end
26
+
27
+ require 'rack'
28
+
29
+ module Rack
30
+ module Handler
31
+ autoload :Thin, 'rack/handler/thin'
32
+ end
33
+ module Adapter
34
+ autoload :Rails, 'rack/adapter/rails'
35
+ end
36
+ end
@@ -0,0 +1,106 @@
1
+ module Thin
2
+ # Control a set of servers.
3
+ # * Generate start and stop commands and run them.
4
+ # * Inject the port number in the pid and log filenames.
5
+ # Servers are started throught the +thin+ commandline script.
6
+ class Cluster
7
+ include Logging
8
+
9
+ # Path to the +thin+ script used to control the servers.
10
+ # Leave this to default to use the one in the path.
11
+ attr_accessor :script
12
+
13
+ # Number of servers in the cluster.
14
+ attr_accessor :size
15
+
16
+ # Command line options passed to the thin script
17
+ attr_accessor :options
18
+
19
+ # Create a new cluster of servers launched using +options+.
20
+ def initialize(options)
21
+ @options = options.merge(:daemonize => true)
22
+ @size = @options.delete(:servers)
23
+ @script = 'thin'
24
+ end
25
+
26
+ def first_port; @options[:port] end
27
+ def address; @options[:address] end
28
+ def pid_file; File.expand_path File.join(@options[:chdir], @options[:pid]) end
29
+ def log_file; File.expand_path File.join(@options[:chdir], @options[:log]) end
30
+
31
+ # Start the servers
32
+ def start
33
+ with_each_server { |port| start_on_port port }
34
+ end
35
+
36
+ # Start the server on a single port
37
+ def start_on_port(port)
38
+ log "Starting #{address}:#{port} ... "
39
+
40
+ run :start, @options, port
41
+ end
42
+
43
+ # Stop the servers
44
+ def stop
45
+ with_each_server { |port| stop_on_port port }
46
+ end
47
+
48
+ # Stop the server running on +port+
49
+ def stop_on_port(port)
50
+ log "Stopping #{address}:#{port} ... "
51
+
52
+ run :stop, @options, port
53
+ end
54
+
55
+ # Stop and start the servers.
56
+ def restart
57
+ stop
58
+ sleep 0.1 # Let's breath a bit shall we ?
59
+ start
60
+ end
61
+
62
+ def log_file_for(port)
63
+ include_port_number log_file, port
64
+ end
65
+
66
+ def pid_file_for(port)
67
+ include_port_number pid_file, port
68
+ end
69
+
70
+ def pid_for(port)
71
+ File.read(pid_file_for(port)).chomp.to_i
72
+ end
73
+
74
+ private
75
+ # Send the command to the +thin+ script
76
+ def run(cmd, options, port)
77
+ shell_cmd = shellify(cmd, options.merge(:port => port, :pid => pid_file_for(port), :log => log_file_for(port)))
78
+ trace shell_cmd
79
+ ouput = `#{shell_cmd}`.chomp
80
+ log ouput unless ouput.empty?
81
+ end
82
+
83
+ # Turn into a runnable shell command
84
+ def shellify(cmd, options)
85
+ shellified_options = options.inject([]) do |args, (name, value)|
86
+ args << case value
87
+ when NilClass
88
+ when TrueClass then "--#{name}"
89
+ else "--#{name.to_s.tr('_', '-')}=#{value.inspect}"
90
+ end
91
+ end
92
+ "#{@script} #{cmd} #{shellified_options.compact.join(' ')}"
93
+ end
94
+
95
+ def with_each_server
96
+ @size.times { |n| yield first_port + n }
97
+ end
98
+
99
+ # Add the port numbers in the filename
100
+ # so each instance get its own file
101
+ def include_port_number(path, port)
102
+ raise ArgumentError, "filename '#{path}' must include an extension" unless path =~ /\./
103
+ path.gsub(/\.(.+)$/) { ".#{port}.#{$1}" }
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,46 @@
1
+ require 'socket'
2
+
3
+ module Thin
4
+ class Connection < EventMachine::Connection
5
+ include Logging
6
+
7
+ attr_accessor :app
8
+
9
+ def post_init
10
+ @request = Request.new
11
+ @response = Response.new
12
+ end
13
+
14
+ def receive_data(data)
15
+ trace { data }
16
+ process if @request.parse(data)
17
+ rescue InvalidRequest => e
18
+ log "Invalid request"
19
+ log_error e
20
+ close_connection
21
+ end
22
+
23
+ def process
24
+ env = @request.env
25
+
26
+ # Add client info to the request env
27
+ env[Request::REMOTE_ADDR] = env[Request::FORWARDED_FOR] || Socket.unpack_sockaddr_in(get_peername)[1]
28
+
29
+ # Process the request
30
+ @response.status, @response.headers, @response.body = @app.call(env)
31
+
32
+ # Send the response
33
+ trace { @response.to_s }
34
+ send_data @response.to_s
35
+
36
+ close_connection_after_writing
37
+
38
+ rescue Object => e
39
+ log "Unexpected error while processing request: #{e.message}"
40
+ log_error e
41
+ close_connection rescue nil
42
+ ensure
43
+ @response.close rescue nil
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,112 @@
1
+ require 'etc'
2
+ require 'daemons'
3
+
4
+ module Process
5
+ # Returns +true+ the process identied by +pid+ is running.
6
+ def running?(pid)
7
+ Process.getpgid(pid) != -1
8
+ rescue Errno::ESRCH
9
+ false
10
+ end
11
+ module_function :running?
12
+ end
13
+
14
+ module Thin
15
+ # Module included in classes that can be turned into a daemon.
16
+ # Handle stuff like:
17
+ # * storing the PID in a file
18
+ # * redirecting output to the log file
19
+ # * changing processs privileges
20
+ # * killing the process gracefully
21
+ module Daemonizable
22
+ attr_accessor :pid_file, :log_file
23
+
24
+ def self.included(base)
25
+ base.extend ClassMethods
26
+ end
27
+
28
+ def pid
29
+ File.exist?(pid_file) ? open(pid_file).read : nil
30
+ end
31
+
32
+ # Turns the current script into a daemon process that detaches from the console.
33
+ def daemonize
34
+ check_plateform_support
35
+ raise ArgumentError, 'You must specify a pid_file to deamonize' unless @pid_file
36
+
37
+ pwd = Dir.pwd # Current directory is changed during daemonization, so store it
38
+
39
+ Daemonize.daemonize(File.expand_path(@log_file))
40
+
41
+ Dir.chdir(pwd)
42
+
43
+ write_pid_file
44
+ at_exit do
45
+ log ">> Exiting!"
46
+ remove_pid_file
47
+ end
48
+ end
49
+
50
+ # Change privileges of the process
51
+ # to the specified user and group.
52
+ def change_privilege(user, group=user)
53
+ check_plateform_support
54
+ log ">> Changing process privilege to #{user}:#{group}"
55
+
56
+ uid, gid = Process.euid, Process.egid
57
+ target_uid = Etc.getpwnam(user).uid
58
+ target_gid = Etc.getgrnam(group).gid
59
+
60
+ if uid != target_uid || gid != target_gid
61
+ # Change process ownership
62
+ Process.initgroups(user, target_gid)
63
+ Process::GID.change_privilege(target_gid)
64
+ Process::UID.change_privilege(target_uid)
65
+ end
66
+ rescue Errno::EPERM => e
67
+ log "Couldn't change user and group to #{user}:#{group}: #{e}"
68
+ end
69
+
70
+ module ClassMethods
71
+ # Kill the process which PID is stored in +pid_file+.
72
+ def kill(pid_file, timeout=60)
73
+ if pid = open(pid_file).read
74
+ pid = pid.to_i
75
+ print "Sending INT signal to process #{pid} ... "
76
+ begin
77
+ Process.kill('INT', pid)
78
+ Timeout.timeout(timeout) do
79
+ sleep 0.1 while Process.running?(pid)
80
+ end
81
+ rescue Timeout::Error
82
+ print "timeout, Sending KILL signal ... "
83
+ Process.kill('KILL', pid)
84
+ end
85
+ puts "stopped!"
86
+ else
87
+ puts "Can't stop process, no PID found in #{@pid_file}"
88
+ end
89
+ rescue Errno::ESRCH # No such process
90
+ puts "process not found!"
91
+ ensure
92
+ File.delete(pid_file) rescue nil
93
+ end
94
+ end
95
+
96
+ private
97
+ def check_plateform_support
98
+ raise RuntimeError, 'Daemonizing not supported on Windows' if RUBY_PLATFORM =~ /mswin/
99
+ end
100
+
101
+ def remove_pid_file
102
+ File.delete(@pid_file) if @pid_file && File.exists?(@pid_file)
103
+ end
104
+
105
+ def write_pid_file
106
+ log ">> Writing PID to #{@pid_file}"
107
+ FileUtils.mkdir_p File.dirname(@pid_file)
108
+ open(@pid_file,"w") { |f| f.write(Process.pid) }
109
+ File.chmod(0644, @pid_file)
110
+ end
111
+ end
112
+ end