friendlyfashion-thin 1.4.1

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