gross 1.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +410 -0
  3. data/README.md +102 -0
  4. data/Rakefile +25 -0
  5. data/bin/thin +6 -0
  6. data/example/adapter.rb +32 -0
  7. data/example/async_app.ru +126 -0
  8. data/example/async_chat.ru +247 -0
  9. data/example/async_tailer.ru +100 -0
  10. data/example/config.ru +22 -0
  11. data/example/monit_sockets +20 -0
  12. data/example/monit_unixsock +20 -0
  13. data/example/myapp.rb +1 -0
  14. data/example/ramaze.ru +12 -0
  15. data/example/thin.god +80 -0
  16. data/example/thin_solaris_smf.erb +36 -0
  17. data/example/thin_solaris_smf.readme.txt +150 -0
  18. data/example/vlad.rake +72 -0
  19. data/ext/thin_parser/common.rl +59 -0
  20. data/ext/thin_parser/ext_help.h +14 -0
  21. data/ext/thin_parser/extconf.rb +6 -0
  22. data/ext/thin_parser/parser.c +1447 -0
  23. data/ext/thin_parser/parser.h +49 -0
  24. data/ext/thin_parser/parser.rl +152 -0
  25. data/ext/thin_parser/thin.c +435 -0
  26. data/lib/rack/adapter/loader.rb +75 -0
  27. data/lib/rack/adapter/rails.rb +178 -0
  28. data/lib/thin.rb +45 -0
  29. data/lib/thin/backends/base.rb +167 -0
  30. data/lib/thin/backends/swiftiply_client.rb +56 -0
  31. data/lib/thin/backends/tcp_server.rb +34 -0
  32. data/lib/thin/backends/unix_server.rb +56 -0
  33. data/lib/thin/command.rb +53 -0
  34. data/lib/thin/connection.rb +215 -0
  35. data/lib/thin/controllers/cluster.rb +178 -0
  36. data/lib/thin/controllers/controller.rb +189 -0
  37. data/lib/thin/controllers/service.rb +76 -0
  38. data/lib/thin/controllers/service.sh.erb +39 -0
  39. data/lib/thin/daemonizing.rb +180 -0
  40. data/lib/thin/headers.rb +40 -0
  41. data/lib/thin/logging.rb +174 -0
  42. data/lib/thin/request.rb +162 -0
  43. data/lib/thin/response.rb +117 -0
  44. data/lib/thin/runner.rb +238 -0
  45. data/lib/thin/server.rb +290 -0
  46. data/lib/thin/stats.html.erb +216 -0
  47. data/lib/thin/stats.rb +52 -0
  48. data/lib/thin/statuses.rb +44 -0
  49. data/lib/thin/version.rb +32 -0
  50. metadata +156 -0
@@ -0,0 +1,75 @@
1
+ module Rack
2
+ class AdapterNotFound < RuntimeError; end
3
+
4
+ # Mapping used to guess which adapter to use in <tt>Adapter.for</tt>.
5
+ # Framework <name> => <file unique to this framework> in order they will
6
+ # be tested.
7
+ # +nil+ for value to never guess.
8
+ # NOTE: If a framework has a file that is not unique, make sure to place
9
+ # it at the end.
10
+ ADAPTERS = [
11
+ [:rack, 'config.ru'],
12
+ [:rails, 'config/environment.rb'],
13
+ [:ramaze, 'start.rb'],
14
+ [:merb, 'config/init.rb'],
15
+ [:file, nil]
16
+ ]
17
+
18
+ module Adapter
19
+ # Guess which adapter to use based on the directory structure
20
+ # or file content.
21
+ # Returns a symbol representing the name of the adapter to use
22
+ # to load the application under <tt>dir/</tt>.
23
+ def self.guess(dir)
24
+ ADAPTERS.each do |adapter, file|
25
+ return adapter if file && ::File.exist?(::File.join(dir, file))
26
+ end
27
+ raise AdapterNotFound, "No adapter found for #{dir}"
28
+ end
29
+
30
+ # Load a Rack application from a Rack config file (.ru).
31
+ def self.load(config)
32
+ rackup_code = ::File.read(config)
33
+ eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, config)
34
+ end
35
+
36
+ # Loads an adapter identified by +name+ using +options+ hash.
37
+ def self.for(name, options={})
38
+ ENV['RACK_ENV'] = options[:environment]
39
+
40
+ case name.to_sym
41
+ when :rack
42
+ return load(::File.join(options[:chdir], "config.ru"))
43
+
44
+ when :rails
45
+ return Rails.new(options.merge(:root => options[:chdir]))
46
+
47
+ when :ramaze
48
+ require "#{options[:chdir]}/start"
49
+
50
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
51
+ Ramaze.start :force => true
52
+
53
+ return Ramaze::Adapter::Base
54
+
55
+ when :merb
56
+ require 'merb-core'
57
+
58
+ Merb::Config.setup(:merb_root => options[:chdir],
59
+ :environment => options[:environment])
60
+ Merb.environment = Merb::Config[:environment]
61
+ Merb.root = Merb::Config[:merb_root]
62
+ Merb::BootLoader.run
63
+
64
+ return Merb::Rack::Application.new
65
+
66
+ when :file
67
+ return Rack::File.new(options[:chdir])
68
+
69
+ else
70
+ raise AdapterNotFound, "Adapter not found: #{name}"
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,178 @@
1
+ require 'cgi'
2
+
3
+ # Adapter to run a Rails app with any supported Rack handler.
4
+ # By default it will try to load the Rails application in the
5
+ # current directory in the development environment.
6
+ #
7
+ # Options:
8
+ # root: Root directory of the Rails app
9
+ # environment: Rails environment to run in (development [default], production or test)
10
+ # prefix: Set the relative URL root.
11
+ #
12
+ # Based on http://fuzed.rubyforge.org/ Rails adapter
13
+ module Rack
14
+ module Adapter
15
+ class Rails
16
+ FILE_METHODS = %w(GET HEAD).freeze
17
+
18
+ def initialize(options = {})
19
+ @root = options[:root] || Dir.pwd
20
+ @env = options[:environment] || 'development'
21
+ @prefix = options[:prefix]
22
+
23
+ load_application
24
+
25
+ @rails_app = self.class.rack_based? ? ActionController::Dispatcher.new : CgiApp.new
26
+ @file_app = Rack::File.new(::File.join(RAILS_ROOT, "public"))
27
+ end
28
+
29
+ def load_application
30
+ ENV['RAILS_ENV'] = @env
31
+
32
+ require "#{@root}/config/environment"
33
+ require 'dispatcher'
34
+
35
+ if @prefix
36
+ if ActionController::Base.respond_to?(:relative_url_root=)
37
+ ActionController::Base.relative_url_root = @prefix # Rails 2.1.1
38
+ else
39
+ ActionController::AbstractRequest.relative_url_root = @prefix
40
+ end
41
+ end
42
+ end
43
+
44
+ def file_exist?(path)
45
+ full_path = ::File.join(@file_app.root, Utils.unescape(path))
46
+ ::File.file?(full_path) && ::File.readable_real?(full_path)
47
+ end
48
+
49
+ def call(env)
50
+ path = env['PATH_INFO'].chomp('/')
51
+ method = env['REQUEST_METHOD']
52
+ cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
53
+
54
+ if FILE_METHODS.include?(method)
55
+ if file_exist?(path) # Serve the file if it's there
56
+ return @file_app.call(env)
57
+ elsif file_exist?(cached_path) # Serve the page cache if it's there
58
+ env['PATH_INFO'] = cached_path
59
+ return @file_app.call(env)
60
+ end
61
+ end
62
+
63
+ # No static file, let Rails handle it
64
+ @rails_app.call(env)
65
+ end
66
+
67
+ def self.rack_based?
68
+ rails_version = ::Rails::VERSION
69
+ return false if rails_version::MAJOR < 2
70
+ return false if rails_version::MAJOR == 2 && rails_version::MINOR < 2
71
+ return false if rails_version::MAJOR == 2 && rails_version::MINOR == 2 && rails_version::TINY < 3
72
+ true # >= 2.2.3
73
+ end
74
+
75
+ protected
76
+ # For Rails pre Rack (2.3)
77
+ class CgiApp
78
+ def call(env)
79
+ request = Request.new(env)
80
+ response = Response.new
81
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
82
+ cgi = CGIWrapper.new(request, response)
83
+
84
+ Dispatcher.dispatch(cgi, session_options, response)
85
+
86
+ response.finish
87
+ end
88
+ end
89
+
90
+ class CGIWrapper < ::CGI
91
+ def initialize(request, response, *args)
92
+ @request = request
93
+ @response = response
94
+ @args = *args
95
+ @input = request.body
96
+
97
+ super *args
98
+ end
99
+
100
+ def header(options = 'text/html')
101
+ if options.is_a?(String)
102
+ @response['Content-Type'] = options unless @response['Content-Type']
103
+ else
104
+ @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
105
+
106
+ @response['Content-Type'] = options.delete('type') || "text/html"
107
+ @response['Content-Type'] += '; charset=' + options.delete('charset') if options['charset']
108
+
109
+ @response['Content-Language'] = options.delete('language') if options['language']
110
+ @response['Expires'] = options.delete('expires') if options['expires']
111
+
112
+ @response.status = options.delete('Status') if options['Status']
113
+
114
+ # Convert 'cookie' header to 'Set-Cookie' headers.
115
+ # Because Set-Cookie header can appear more the once in the response body,
116
+ # we store it in a line break seperated string that will be translated to
117
+ # multiple Set-Cookie header by the handler.
118
+ if cookie = options.delete('cookie')
119
+ cookies = []
120
+
121
+ case cookie
122
+ when Array then cookie.each { |c| cookies << c.to_s }
123
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
124
+ else cookies << cookie.to_s
125
+ end
126
+
127
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
128
+
129
+ @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact
130
+ # See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69
131
+ if Thin.ruby_18?
132
+ @response['Set-Cookie'].flatten!
133
+ else
134
+ @response['Set-Cookie'] = @response['Set-Cookie'].join("\n")
135
+ end
136
+ end
137
+
138
+ options.each { |k, v| @response[k] = v }
139
+ end
140
+
141
+ ''
142
+ end
143
+
144
+ def params
145
+ @params ||= @request.params
146
+ end
147
+
148
+ def cookies
149
+ @request.cookies
150
+ end
151
+
152
+ def query_string
153
+ @request.query_string
154
+ end
155
+
156
+ # Used to wrap the normal args variable used inside CGI.
157
+ def args
158
+ @args
159
+ end
160
+
161
+ # Used to wrap the normal env_table variable used inside CGI.
162
+ def env_table
163
+ @request.env
164
+ end
165
+
166
+ # Used to wrap the normal stdinput variable used inside CGI.
167
+ def stdinput
168
+ @input
169
+ end
170
+
171
+ def stdoutput
172
+ STDERR.puts 'stdoutput should not be used.'
173
+ @response.body
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,45 @@
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
+ require "thin/version"
37
+ require "thin/statuses"
38
+ require "rack/adapter/loader"
39
+ require "thin_parser"
40
+
41
+ module Rack
42
+ module Adapter
43
+ autoload :Rails, "rack/adapter/rails"
44
+ end
45
+ end
@@ -0,0 +1,167 @@
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
+ # * monitoring of the active connections.
7
+ #
8
+ # == Implementing your own backend
9
+ # You can create your own minimal backend by inheriting this class and
10
+ # defining the +connect+ and +disconnect+ method.
11
+ # If your backend is not based on EventMachine you also need to redefine
12
+ # the +start+, +stop+, <tt>stop!</tt> and +config+ methods.
13
+ class Base
14
+ # Server serving the connections throught the backend
15
+ attr_accessor :server
16
+
17
+ # Maximum time for incoming data to arrive
18
+ attr_accessor :timeout
19
+
20
+ # Maximum number of file or socket descriptors that the server may open.
21
+ attr_accessor :maximum_connections
22
+
23
+ # Maximum number of connections that can be persistent
24
+ attr_accessor :maximum_persistent_connections
25
+
26
+ #allows setting of the eventmachine threadpool size
27
+ attr_reader :threadpool_size
28
+ def threadpool_size=(size)
29
+ @threadpool_size = size
30
+ EventMachine.threadpool_size = size
31
+ end
32
+
33
+ # Allow using threads in the backend.
34
+ attr_writer :threaded
35
+ def threaded?; @threaded end
36
+
37
+ # Allow using SSL in the backend.
38
+ attr_writer :ssl, :ssl_options
39
+ def ssl?; @ssl end
40
+
41
+ # Number of persistent connections currently opened
42
+ attr_accessor :persistent_connection_count
43
+
44
+ # Disable the use of epoll under Linux
45
+ attr_accessor :no_epoll
46
+
47
+ def initialize
48
+ @connections = {}
49
+ @timeout = Server::DEFAULT_TIMEOUT
50
+ @persistent_connection_count = 0
51
+ @maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS
52
+ @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
53
+ @no_epoll = false
54
+ @ssl = nil
55
+ @threaded = nil
56
+ @started_reactor = false
57
+ end
58
+
59
+ # Start the backend and connect it.
60
+ def start
61
+ @stopping = false
62
+ starter = proc do
63
+ connect
64
+ yield if block_given?
65
+ @running = true
66
+ end
67
+
68
+ # Allow for early run up of eventmachine.
69
+ if EventMachine.reactor_running?
70
+ starter.call
71
+ else
72
+ @started_reactor = true
73
+ EventMachine.run(&starter)
74
+ end
75
+ end
76
+
77
+ # Stop of the backend from accepting new connections.
78
+ def stop
79
+ @running = false
80
+ @stopping = true
81
+
82
+ # Do not accept anymore connection
83
+ disconnect
84
+ # Close idle persistent connections
85
+ @connections.each_value { |connection| connection.close_connection if connection.idle? }
86
+ stop! if @connections.empty?
87
+ end
88
+
89
+ # Force stop of the backend NOW, too bad for the current connections.
90
+ def stop!
91
+ @running = false
92
+ @stopping = false
93
+
94
+ EventMachine.stop if @started_reactor && EventMachine.reactor_running?
95
+ @connections.each_value { |connection| connection.close_connection }
96
+ close
97
+ end
98
+
99
+ # Configure the backend. This method will be called before droping superuser privileges,
100
+ # so you can do crazy stuff that require godlike powers here.
101
+ def config
102
+ # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
103
+ EventMachine.epoll unless @no_epoll
104
+
105
+ # Set the maximum number of socket descriptors that the server may open.
106
+ # The process needs to have required privilege to set it higher the 1024 on
107
+ # some systems.
108
+ @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
109
+ end
110
+
111
+ # Free up resources used by the backend.
112
+ def close
113
+ end
114
+
115
+ # Returns +true+ if the backend is connected and running.
116
+ def running?
117
+ @running
118
+ end
119
+
120
+ def started_reactor?
121
+ @started_reactor
122
+ end
123
+
124
+ # Called by a connection when it's unbinded.
125
+ def connection_finished(connection)
126
+ @persistent_connection_count -= 1 if connection.can_persist?
127
+ @connections.delete(connection.__id__)
128
+
129
+ # Finalize gracefull stop if there's no more active connection.
130
+ stop! if @stopping && @connections.empty?
131
+ end
132
+
133
+ # Returns +true+ if no active connection.
134
+ def empty?
135
+ @connections.empty?
136
+ end
137
+
138
+ # Number of active connections.
139
+ def size
140
+ @connections.size
141
+ end
142
+
143
+ protected
144
+ # Initialize a new connection to a client.
145
+ def initialize_connection(connection)
146
+ connection.backend = self
147
+ connection.app = @server.app
148
+ connection.comm_inactivity_timeout = @timeout
149
+ connection.threaded = @threaded
150
+
151
+ if @ssl
152
+ connection.start_tls(@ssl_options)
153
+ end
154
+
155
+ # We control the number of persistent connections by keeping
156
+ # a count of the total one allowed yet.
157
+ if @persistent_connection_count < @maximum_persistent_connections
158
+ connection.can_persist!
159
+ @persistent_connection_count += 1
160
+ end
161
+
162
+ @connections[connection.__id__] = connection
163
+ end
164
+
165
+ end
166
+ end
167
+ end