grockit-thin 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. data/CHANGELOG +220 -0
  2. data/COMMITTERS +3 -0
  3. data/COPYING +18 -0
  4. data/README +77 -0
  5. data/Rakefile +13 -0
  6. data/benchmark/abc +51 -0
  7. data/benchmark/benchmarker.rb +80 -0
  8. data/benchmark/runner +79 -0
  9. data/bin/thin +6 -0
  10. data/example/adapter.rb +32 -0
  11. data/example/config.ru +23 -0
  12. data/example/monit_sockets +20 -0
  13. data/example/monit_unixsock +20 -0
  14. data/example/myapp.rb +1 -0
  15. data/example/ramaze.ru +12 -0
  16. data/example/thin.god +80 -0
  17. data/example/thin_solaris_smf.erb +36 -0
  18. data/example/thin_solaris_smf.readme.txt +150 -0
  19. data/example/vlad.rake +61 -0
  20. data/ext/thin_parser/common.rl +55 -0
  21. data/ext/thin_parser/ext_help.h +14 -0
  22. data/ext/thin_parser/extconf.rb +6 -0
  23. data/ext/thin_parser/parser.c +1218 -0
  24. data/ext/thin_parser/parser.h +49 -0
  25. data/ext/thin_parser/parser.rl +159 -0
  26. data/ext/thin_parser/thin.c +433 -0
  27. data/lib/rack/adapter/loader.rb +78 -0
  28. data/lib/rack/adapter/rails.rb +167 -0
  29. data/lib/rack/handler/thin.rb +18 -0
  30. data/lib/thin.rb +49 -0
  31. data/lib/thin/backends/base.rb +135 -0
  32. data/lib/thin/backends/swiftiply_client.rb +56 -0
  33. data/lib/thin/backends/tcp_server.rb +29 -0
  34. data/lib/thin/backends/unix_server.rb +51 -0
  35. data/lib/thin/command.rb +52 -0
  36. data/lib/thin/connection.rb +178 -0
  37. data/lib/thin/controllers/cluster.rb +121 -0
  38. data/lib/thin/controllers/controller.rb +182 -0
  39. data/lib/thin/controllers/service.rb +75 -0
  40. data/lib/thin/controllers/service.sh.erb +39 -0
  41. data/lib/thin/daemonizing.rb +163 -0
  42. data/lib/thin/headers.rb +31 -0
  43. data/lib/thin/logging.rb +54 -0
  44. data/lib/thin/request.rb +144 -0
  45. data/lib/thin/response.rb +96 -0
  46. data/lib/thin/runner.rb +208 -0
  47. data/lib/thin/server.rb +241 -0
  48. data/lib/thin/stats.html.erb +216 -0
  49. data/lib/thin/stats.rb +52 -0
  50. data/lib/thin/statuses.rb +43 -0
  51. data/lib/thin/version.rb +32 -0
  52. data/spec/backends/swiftiply_client_spec.rb +66 -0
  53. data/spec/backends/tcp_server_spec.rb +33 -0
  54. data/spec/backends/unix_server_spec.rb +37 -0
  55. data/spec/command_spec.rb +20 -0
  56. data/spec/configs/cluster.yml +9 -0
  57. data/spec/configs/single.yml +9 -0
  58. data/spec/connection_spec.rb +105 -0
  59. data/spec/controllers/cluster_spec.rb +179 -0
  60. data/spec/controllers/controller_spec.rb +121 -0
  61. data/spec/controllers/service_spec.rb +50 -0
  62. data/spec/daemonizing_spec.rb +192 -0
  63. data/spec/headers_spec.rb +29 -0
  64. data/spec/logging_spec.rb +46 -0
  65. data/spec/perf/request_perf_spec.rb +50 -0
  66. data/spec/perf/response_perf_spec.rb +19 -0
  67. data/spec/perf/server_perf_spec.rb +39 -0
  68. data/spec/rack/loader_spec.rb +29 -0
  69. data/spec/rack/rails_adapter_spec.rb +106 -0
  70. data/spec/rails_app/app/controllers/application.rb +10 -0
  71. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  72. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  73. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  74. data/spec/rails_app/config/boot.rb +109 -0
  75. data/spec/rails_app/config/environment.rb +64 -0
  76. data/spec/rails_app/config/environments/development.rb +18 -0
  77. data/spec/rails_app/config/environments/production.rb +19 -0
  78. data/spec/rails_app/config/environments/test.rb +22 -0
  79. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  80. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  81. data/spec/rails_app/config/routes.rb +35 -0
  82. data/spec/rails_app/public/404.html +30 -0
  83. data/spec/rails_app/public/422.html +30 -0
  84. data/spec/rails_app/public/500.html +30 -0
  85. data/spec/rails_app/public/dispatch.cgi +10 -0
  86. data/spec/rails_app/public/dispatch.fcgi +24 -0
  87. data/spec/rails_app/public/dispatch.rb +10 -0
  88. data/spec/rails_app/public/favicon.ico +0 -0
  89. data/spec/rails_app/public/images/rails.png +0 -0
  90. data/spec/rails_app/public/index.html +277 -0
  91. data/spec/rails_app/public/javascripts/application.js +2 -0
  92. data/spec/rails_app/public/javascripts/controls.js +963 -0
  93. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  94. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  95. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  96. data/spec/rails_app/public/robots.txt +5 -0
  97. data/spec/rails_app/script/about +3 -0
  98. data/spec/rails_app/script/console +3 -0
  99. data/spec/rails_app/script/destroy +3 -0
  100. data/spec/rails_app/script/generate +3 -0
  101. data/spec/rails_app/script/performance/benchmarker +3 -0
  102. data/spec/rails_app/script/performance/profiler +3 -0
  103. data/spec/rails_app/script/performance/request +3 -0
  104. data/spec/rails_app/script/plugin +3 -0
  105. data/spec/rails_app/script/process/inspector +3 -0
  106. data/spec/rails_app/script/process/reaper +3 -0
  107. data/spec/rails_app/script/process/spawner +3 -0
  108. data/spec/rails_app/script/runner +3 -0
  109. data/spec/rails_app/script/server +3 -0
  110. data/spec/request/mongrel_spec.rb +39 -0
  111. data/spec/request/parser_spec.rb +191 -0
  112. data/spec/request/persistent_spec.rb +35 -0
  113. data/spec/request/processing_spec.rb +45 -0
  114. data/spec/response_spec.rb +76 -0
  115. data/spec/runner_spec.rb +167 -0
  116. data/spec/server/builder_spec.rb +44 -0
  117. data/spec/server/pipelining_spec.rb +109 -0
  118. data/spec/server/robustness_spec.rb +34 -0
  119. data/spec/server/stopping_spec.rb +45 -0
  120. data/spec/server/swiftiply.yml +6 -0
  121. data/spec/server/swiftiply_spec.rb +32 -0
  122. data/spec/server/tcp_spec.rb +57 -0
  123. data/spec/server/threaded_spec.rb +27 -0
  124. data/spec/server/unix_socket_spec.rb +26 -0
  125. data/spec/server_spec.rb +96 -0
  126. data/spec/spec_helper.rb +219 -0
  127. data/tasks/announce.rake +22 -0
  128. data/tasks/deploy.rake +16 -0
  129. data/tasks/email.erb +30 -0
  130. data/tasks/ext.rake +42 -0
  131. data/tasks/gem.rake +102 -0
  132. data/tasks/rdoc.rake +25 -0
  133. data/tasks/site.rake +15 -0
  134. data/tasks/spec.rake +48 -0
  135. data/tasks/stats.rake +28 -0
  136. metadata +240 -0
@@ -0,0 +1,78 @@
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
+ [:rails, 'config/environment.rb'],
12
+ [:ramaze, 'start.rb'],
13
+ [:halcyon, 'runner.ru'],
14
+ [:merb, 'config/init.rb'],
15
+ [:mack, 'config/app_config/default.yml'],
16
+ [:file, nil]
17
+ ]
18
+
19
+ module Adapter
20
+ # Guess which adapter to use based on the directory structure
21
+ # or file content.
22
+ # Returns a symbol representing the name of the adapter to use
23
+ # to load the application under <tt>dir/</tt>.
24
+ def self.guess(dir)
25
+ ADAPTERS.each do |adapter, file|
26
+ return adapter if file && ::File.exist?(::File.join(dir, file))
27
+ end
28
+ raise AdapterNotFound, "No adapter found for #{dir}"
29
+ end
30
+
31
+ # Loads an adapter identified by +name+ using +options+ hash.
32
+ def self.for(name, options={})
33
+ case name.to_sym
34
+ when :rails
35
+ return Rails.new(options.merge(:root => options[:chdir]))
36
+
37
+ when :ramaze
38
+ require "#{options[:chdir]}/start"
39
+
40
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
41
+ Ramaze.start :force => true
42
+
43
+ return Ramaze::Adapter::Base
44
+
45
+ when :merb
46
+ require 'merb-core'
47
+
48
+ Merb::Config.setup(:merb_root => options[:chdir],
49
+ :environment => options[:environment])
50
+ Merb.environment = Merb::Config[:environment]
51
+ Merb.root = Merb::Config[:merb_root]
52
+ Merb::BootLoader.run
53
+
54
+ return Merb::Rack::Application.new
55
+
56
+ when :halcyon
57
+ require 'halcyon'
58
+
59
+ $:.unshift(Halcyon.root/'lib')
60
+
61
+ return Halcyon::Runner.new
62
+
63
+ when :mack
64
+ ENV["MACK_ENV"] = options[:environment]
65
+ load(::File.join(options[:chdir], "Rakefile"))
66
+ require 'mack'
67
+ return Mack::Utils::Server.build_app
68
+
69
+ when :file
70
+ return Rack::File.new(options[:chdir])
71
+
72
+ else
73
+ raise AdapterNotFound, "Adapter not found: #{name}"
74
+
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,167 @@
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
+ @file_server = Rack::File.new(::File.join(RAILS_ROOT, "public"))
26
+ end
27
+
28
+ def load_application
29
+ ENV['RAILS_ENV'] = @env
30
+
31
+ require "#{@root}/config/environment"
32
+ require 'dispatcher'
33
+
34
+ ActionController::AbstractRequest.relative_url_root = @prefix if @prefix
35
+ end
36
+
37
+ # TODO refactor this in File#can_serve?(path) ??
38
+ def file_exist?(path)
39
+ full_path = ::File.join(@file_server.root, Utils.unescape(path))
40
+ ::File.file?(full_path) && ::File.readable?(full_path)
41
+ end
42
+
43
+ def serve_file(env)
44
+ @file_server.call(env)
45
+ end
46
+
47
+ def serve_rails(env)
48
+ request = Request.new(env)
49
+ response = Response.new
50
+
51
+ session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
52
+ cgi = CGIWrapper.new(request, response)
53
+
54
+ Dispatcher.dispatch(cgi, session_options, response)
55
+
56
+ response.finish
57
+ end
58
+
59
+ def call(env)
60
+ path = env['PATH_INFO'].chomp('/')
61
+ method = env['REQUEST_METHOD']
62
+ cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension
63
+
64
+ if FILE_METHODS.include?(method)
65
+ if file_exist?(path) # Serve the file if it's there
66
+ return serve_file(env)
67
+ elsif file_exist?(cached_path) # Serve the page cache if it's there
68
+ env['PATH_INFO'] = cached_path
69
+ return serve_file(env)
70
+ end
71
+ end
72
+
73
+ # No static file, let Rails handle it
74
+ serve_rails(env)
75
+ end
76
+
77
+ protected
78
+
79
+ class CGIWrapper < ::CGI
80
+ def initialize(request, response, *args)
81
+ @request = request
82
+ @response = response
83
+ @args = *args
84
+ @input = request.body
85
+
86
+ super *args
87
+ end
88
+
89
+ def header(options = "text/html")
90
+ if options.is_a?(String)
91
+ @response['Content-Type'] = options unless @response['Content-Type']
92
+ else
93
+ @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length']
94
+
95
+ @response['Content-Type'] = options.delete('type') || "text/html"
96
+ @response['Content-Type'] += "; charset=" + options.delete('charset') if options['charset']
97
+
98
+ @response['Content-Language'] = options.delete('language') if options['language']
99
+ @response['Expires'] = options.delete('expires') if options['expires']
100
+
101
+ @response.status = options.delete('Status') if options['Status']
102
+
103
+ # Convert 'cookie' header to 'Set-Cookie' headers.
104
+ # Because Set-Cookie header can appear more the once in the response body,
105
+ # we store it in a line break seperated string that will be translated to
106
+ # multiple Set-Cookie header by the handler.
107
+ if cookie = options.delete('cookie')
108
+ cookies = []
109
+
110
+ case cookie
111
+ when Array then cookie.each { |c| cookies << c.to_s }
112
+ when Hash then cookie.each { |_, c| cookies << c.to_s }
113
+ else cookies << cookie.to_s
114
+ end
115
+
116
+ @output_cookies.each { |c| cookies << c.to_s } if @output_cookies
117
+
118
+ @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact
119
+ # See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69
120
+ if Thin.ruby_18?
121
+ @response['Set-Cookie'].flatten!
122
+ else
123
+ @response['Set-Cookie'] = @response['Set-Cookie'].join("\n")
124
+ end
125
+ end
126
+
127
+ options.each { |k,v| @response[k] = v }
128
+ end
129
+
130
+ ""
131
+ end
132
+
133
+ def params
134
+ @params ||= @request.params
135
+ end
136
+
137
+ def cookies
138
+ @request.cookies
139
+ end
140
+
141
+ def query_string
142
+ @request.query_string
143
+ end
144
+
145
+ # Used to wrap the normal args variable used inside CGI.
146
+ def args
147
+ @args
148
+ end
149
+
150
+ # Used to wrap the normal env_table variable used inside CGI.
151
+ def env_table
152
+ @request.env
153
+ end
154
+
155
+ # Used to wrap the normal stdinput variable used inside CGI.
156
+ def stdinput
157
+ @input
158
+ end
159
+
160
+ def stdoutput
161
+ STDERR.puts "stdoutput should not be used."
162
+ @response.body
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,18 @@
1
+ module Rack
2
+ module Handler
3
+ # Rack Handler stricly to be able to use Thin through the rackup command.
4
+ # To do so, simply require 'thin' in your Rack config file and run like this
5
+ #
6
+ # rackup --server thin
7
+ #
8
+ class Thin
9
+ def self.run(app, options={})
10
+ server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
11
+ options[:Port] || 8080,
12
+ app)
13
+ yield server if block_given?
14
+ server.start
15
+ end
16
+ end
17
+ end
18
+ end
data/lib/thin.rb ADDED
@@ -0,0 +1,49 @@
1
+ $:.unshift 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
+ autoload :Command, 'thin/command'
15
+ autoload :Connection, 'thin/connection'
16
+ autoload :Daemonizable, 'thin/daemonizing'
17
+ autoload :Logging, 'thin/logging'
18
+ autoload :Headers, 'thin/headers'
19
+ autoload :Request, 'thin/request'
20
+ autoload :Response, 'thin/response'
21
+ autoload :Runner, 'thin/runner'
22
+ autoload :Server, 'thin/server'
23
+ autoload :Stats, 'thin/stats'
24
+
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
+ end
31
+
32
+ module Controllers
33
+ autoload :Cluster, 'thin/controllers/cluster'
34
+ autoload :Controller, 'thin/controllers/controller'
35
+ autoload :Service, 'thin/controllers/service'
36
+ end
37
+ end
38
+
39
+ require 'rack'
40
+ require 'rack/adapter/loader'
41
+
42
+ module Rack
43
+ module Handler
44
+ autoload :Thin, 'rack/handler/thin'
45
+ end
46
+ module Adapter
47
+ autoload :Rails, 'rack/adapter/rails'
48
+ end
49
+ end
@@ -0,0 +1,135 @@
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
+ # Number of persistent connections currently opened
31
+ attr_accessor :persistent_connection_count
32
+
33
+ # Disable the use of epoll under Linux
34
+ attr_accessor :no_epoll
35
+
36
+ def initialize
37
+ @connections = []
38
+ @timeout = Server::DEFAULT_TIMEOUT
39
+ @persistent_connection_count = 0
40
+ @maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS
41
+ @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
42
+ @no_epoll = false
43
+ end
44
+
45
+ # Start the backend and connect it.
46
+ def start
47
+ @stopping = false
48
+
49
+ EventMachine.run do
50
+ connect
51
+ @running = true
52
+ end
53
+ end
54
+
55
+ # Stop of the backend from accepting new connections.
56
+ def stop
57
+ @running = false
58
+ @stopping = true
59
+
60
+ # Do not accept anymore connection
61
+ disconnect
62
+ stop! if @connections.empty?
63
+ end
64
+
65
+ # Force stop of the backend NOW, too bad for the current connections.
66
+ def stop!
67
+ @running = false
68
+ @stopping = false
69
+
70
+ EventMachine.stop if EventMachine.reactor_running?
71
+ @connections.each { |connection| connection.close_connection }
72
+ close
73
+ end
74
+
75
+ # Configure the backend. This method will be called before droping superuser privileges,
76
+ # so you can do crazy stuff that require godlike powers here.
77
+ def config
78
+ # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
79
+ EventMachine.epoll unless @no_epoll
80
+
81
+ # Set the maximum number of socket descriptors that the server may open.
82
+ # The process needs to have required privilege to set it higher the 1024 on
83
+ # some systems.
84
+ @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
85
+ end
86
+
87
+ # Free up resources used by the backend.
88
+ def close
89
+ end
90
+
91
+ # Returns +true+ if the backend is connected and running.
92
+ def running?
93
+ @running
94
+ end
95
+
96
+ # Called by a connection when it's unbinded.
97
+ def connection_finished(connection)
98
+ @persistent_connection_count -= 1 if connection.can_persist?
99
+ @connections.delete(connection)
100
+
101
+ # Finalize gracefull stop if there's no more active connection.
102
+ stop! if @stopping && @connections.empty?
103
+ end
104
+
105
+ # Returns +true+ if no active connection.
106
+ def empty?
107
+ @connections.empty?
108
+ end
109
+
110
+ # Number of active connections.
111
+ def size
112
+ @connections.size
113
+ end
114
+
115
+ protected
116
+ # Initialize a new connection to a client.
117
+ def initialize_connection(connection)
118
+ connection.backend = self
119
+ connection.app = @server.app
120
+ connection.comm_inactivity_timeout = @timeout
121
+ connection.threaded = @threaded
122
+
123
+ # We control the number of persistent connections by keeping
124
+ # a count of the total one allowed yet.
125
+ if @persistent_connection_count < @maximum_persistent_connections
126
+ connection.can_persist!
127
+ @persistent_connection_count += 1
128
+ end
129
+
130
+ @connections << connection
131
+ end
132
+
133
+ end
134
+ end
135
+ end