steamcannon-thin 1.2.8

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 (137) hide show
  1. data/CHANGELOG +288 -0
  2. data/COPYING +18 -0
  3. data/README +69 -0
  4. data/Rakefile +44 -0
  5. data/benchmark/abc +51 -0
  6. data/benchmark/benchmarker.rb +80 -0
  7. data/benchmark/runner +82 -0
  8. data/bin/thin +6 -0
  9. data/example/adapter.rb +32 -0
  10. data/example/async_app.ru +126 -0
  11. data/example/async_chat.ru +247 -0
  12. data/example/async_tailer.ru +100 -0
  13. data/example/config.ru +22 -0
  14. data/example/monit_sockets +20 -0
  15. data/example/monit_unixsock +20 -0
  16. data/example/myapp.rb +1 -0
  17. data/example/ramaze.ru +12 -0
  18. data/example/thin.god +80 -0
  19. data/example/thin_solaris_smf.erb +36 -0
  20. data/example/thin_solaris_smf.readme.txt +150 -0
  21. data/example/vlad.rake +64 -0
  22. data/ext/thin_parser/common.rl +55 -0
  23. data/ext/thin_parser/ext_help.h +14 -0
  24. data/ext/thin_parser/extconf.rb +6 -0
  25. data/ext/thin_parser/parser.c +1249 -0
  26. data/ext/thin_parser/parser.h +49 -0
  27. data/ext/thin_parser/parser.rl +157 -0
  28. data/ext/thin_parser/thin.c +436 -0
  29. data/lib/rack/adapter/loader.rb +91 -0
  30. data/lib/rack/adapter/rails.rb +183 -0
  31. data/lib/thin.rb +56 -0
  32. data/lib/thin/backends/base.rb +149 -0
  33. data/lib/thin/backends/swiftiply_client.rb +56 -0
  34. data/lib/thin/backends/tcp_server.rb +29 -0
  35. data/lib/thin/backends/unix_server.rb +51 -0
  36. data/lib/thin/command.rb +53 -0
  37. data/lib/thin/connection.rb +224 -0
  38. data/lib/thin/controllers/cluster.rb +178 -0
  39. data/lib/thin/controllers/controller.rb +188 -0
  40. data/lib/thin/controllers/service.rb +75 -0
  41. data/lib/thin/controllers/service.sh.erb +39 -0
  42. data/lib/thin/daemonizing.rb +180 -0
  43. data/lib/thin/headers.rb +39 -0
  44. data/lib/thin/logging.rb +54 -0
  45. data/lib/thin/request.rb +156 -0
  46. data/lib/thin/response.rb +101 -0
  47. data/lib/thin/runner.rb +220 -0
  48. data/lib/thin/server.rb +253 -0
  49. data/lib/thin/stats.html.erb +216 -0
  50. data/lib/thin/stats.rb +52 -0
  51. data/lib/thin/statuses.rb +43 -0
  52. data/lib/thin/version.rb +32 -0
  53. data/lib/thin_parser.so +0 -0
  54. data/spec/backends/swiftiply_client_spec.rb +66 -0
  55. data/spec/backends/tcp_server_spec.rb +33 -0
  56. data/spec/backends/unix_server_spec.rb +37 -0
  57. data/spec/command_spec.rb +25 -0
  58. data/spec/configs/cluster.yml +9 -0
  59. data/spec/configs/single.yml +9 -0
  60. data/spec/connection_spec.rb +106 -0
  61. data/spec/controllers/cluster_spec.rb +267 -0
  62. data/spec/controllers/controller_spec.rb +129 -0
  63. data/spec/controllers/service_spec.rb +50 -0
  64. data/spec/daemonizing_spec.rb +196 -0
  65. data/spec/headers_spec.rb +40 -0
  66. data/spec/logging_spec.rb +46 -0
  67. data/spec/perf/request_perf_spec.rb +50 -0
  68. data/spec/perf/response_perf_spec.rb +19 -0
  69. data/spec/perf/server_perf_spec.rb +39 -0
  70. data/spec/rack/loader_spec.rb +42 -0
  71. data/spec/rack/rails_adapter_spec.rb +173 -0
  72. data/spec/rails_app/app/controllers/application.rb +10 -0
  73. data/spec/rails_app/app/controllers/simple_controller.rb +19 -0
  74. data/spec/rails_app/app/helpers/application_helper.rb +3 -0
  75. data/spec/rails_app/app/views/simple/index.html.erb +15 -0
  76. data/spec/rails_app/config/boot.rb +109 -0
  77. data/spec/rails_app/config/environment.rb +64 -0
  78. data/spec/rails_app/config/environments/development.rb +18 -0
  79. data/spec/rails_app/config/environments/production.rb +19 -0
  80. data/spec/rails_app/config/environments/test.rb +22 -0
  81. data/spec/rails_app/config/initializers/inflections.rb +10 -0
  82. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  83. data/spec/rails_app/config/routes.rb +35 -0
  84. data/spec/rails_app/public/404.html +30 -0
  85. data/spec/rails_app/public/422.html +30 -0
  86. data/spec/rails_app/public/500.html +30 -0
  87. data/spec/rails_app/public/dispatch.cgi +10 -0
  88. data/spec/rails_app/public/dispatch.fcgi +24 -0
  89. data/spec/rails_app/public/dispatch.rb +10 -0
  90. data/spec/rails_app/public/favicon.ico +0 -0
  91. data/spec/rails_app/public/images/rails.png +0 -0
  92. data/spec/rails_app/public/index.html +277 -0
  93. data/spec/rails_app/public/javascripts/application.js +2 -0
  94. data/spec/rails_app/public/javascripts/controls.js +963 -0
  95. data/spec/rails_app/public/javascripts/dragdrop.js +972 -0
  96. data/spec/rails_app/public/javascripts/effects.js +1120 -0
  97. data/spec/rails_app/public/javascripts/prototype.js +4225 -0
  98. data/spec/rails_app/public/robots.txt +5 -0
  99. data/spec/rails_app/script/about +3 -0
  100. data/spec/rails_app/script/console +3 -0
  101. data/spec/rails_app/script/destroy +3 -0
  102. data/spec/rails_app/script/generate +3 -0
  103. data/spec/rails_app/script/performance/benchmarker +3 -0
  104. data/spec/rails_app/script/performance/profiler +3 -0
  105. data/spec/rails_app/script/performance/request +3 -0
  106. data/spec/rails_app/script/plugin +3 -0
  107. data/spec/rails_app/script/process/inspector +3 -0
  108. data/spec/rails_app/script/process/reaper +3 -0
  109. data/spec/rails_app/script/process/spawner +3 -0
  110. data/spec/rails_app/script/runner +3 -0
  111. data/spec/rails_app/script/server +3 -0
  112. data/spec/request/mongrel_spec.rb +39 -0
  113. data/spec/request/parser_spec.rb +254 -0
  114. data/spec/request/persistent_spec.rb +35 -0
  115. data/spec/request/processing_spec.rb +50 -0
  116. data/spec/response_spec.rb +91 -0
  117. data/spec/runner_spec.rb +168 -0
  118. data/spec/server/builder_spec.rb +44 -0
  119. data/spec/server/pipelining_spec.rb +110 -0
  120. data/spec/server/robustness_spec.rb +34 -0
  121. data/spec/server/stopping_spec.rb +55 -0
  122. data/spec/server/swiftiply.yml +6 -0
  123. data/spec/server/swiftiply_spec.rb +32 -0
  124. data/spec/server/tcp_spec.rb +57 -0
  125. data/spec/server/threaded_spec.rb +27 -0
  126. data/spec/server/unix_socket_spec.rb +26 -0
  127. data/spec/server_spec.rb +100 -0
  128. data/spec/spec_helper.rb +220 -0
  129. data/tasks/announce.rake +22 -0
  130. data/tasks/deploy.rake +13 -0
  131. data/tasks/email.erb +30 -0
  132. data/tasks/gem.rake +66 -0
  133. data/tasks/rdoc.rake +25 -0
  134. data/tasks/site.rake +15 -0
  135. data/tasks/spec.rake +43 -0
  136. data/tasks/stats.rake +28 -0
  137. metadata +251 -0
@@ -0,0 +1,91 @@
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
+ [:halcyon, 'runner.ru'],
15
+ [:merb, 'config/init.rb'],
16
+ [:mack, 'config/app_config/default.yml'],
17
+ [:mack, 'config/configatron/default.rb'],
18
+ [:file, nil]
19
+ ]
20
+
21
+ module Adapter
22
+ # Guess which adapter to use based on the directory structure
23
+ # or file content.
24
+ # Returns a symbol representing the name of the adapter to use
25
+ # to load the application under <tt>dir/</tt>.
26
+ def self.guess(dir)
27
+ ADAPTERS.each do |adapter, file|
28
+ return adapter if file && ::File.exist?(::File.join(dir, file))
29
+ end
30
+ raise AdapterNotFound, "No adapter found for #{dir}"
31
+ end
32
+
33
+ # Load a Rack application from a Rack config file (.ru).
34
+ def self.load(config)
35
+ rackup_code = ::File.read(config)
36
+ eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, config)
37
+ end
38
+
39
+ # Loads an adapter identified by +name+ using +options+ hash.
40
+ def self.for(name, options={})
41
+ ENV['RACK_ENV'] = options[:environment]
42
+
43
+ case name.to_sym
44
+ when :rack
45
+ return load(::File.join(options[:chdir], "config.ru"))
46
+
47
+ when :rails
48
+ return Rails.new(options.merge(:root => options[:chdir]))
49
+
50
+ when :ramaze
51
+ require "#{options[:chdir]}/start"
52
+
53
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
54
+ Ramaze.start :force => true
55
+
56
+ return Ramaze::Adapter::Base
57
+
58
+ when :merb
59
+ require 'merb-core'
60
+
61
+ Merb::Config.setup(:merb_root => options[:chdir],
62
+ :environment => options[:environment])
63
+ Merb.environment = Merb::Config[:environment]
64
+ Merb.root = Merb::Config[:merb_root]
65
+ Merb::BootLoader.run
66
+
67
+ return Merb::Rack::Application.new
68
+
69
+ when :halcyon
70
+ require 'halcyon'
71
+
72
+ $:.unshift(Halcyon.root/'lib')
73
+
74
+ return Halcyon::Runner.new
75
+
76
+ when :mack
77
+ ENV["MACK_ENV"] = options[:environment]
78
+ load(::File.join(options[:chdir], "Rakefile"))
79
+ require 'mack'
80
+ return Mack::Utils::Server.build_app
81
+
82
+ when :file
83
+ return Rack::File.new(options[:chdir])
84
+
85
+ else
86
+ raise AdapterNotFound, "Adapter not found: #{name}"
87
+
88
+ end
89
+ end
90
+ end
91
+ 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,56 @@
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
+ ROOT = File.expand_path(File.dirname(__FILE__))
12
+
13
+ autoload :Command, "#{ROOT}/thin/command"
14
+ autoload :Connection, "#{ROOT}/thin/connection"
15
+ autoload :Daemonizable, "#{ROOT}/thin/daemonizing"
16
+ autoload :Logging, "#{ROOT}/thin/logging"
17
+ autoload :Headers, "#{ROOT}/thin/headers"
18
+ autoload :Request, "#{ROOT}/thin/request"
19
+ autoload :Response, "#{ROOT}/thin/response"
20
+ autoload :Runner, "#{ROOT}/thin/runner"
21
+ autoload :Server, "#{ROOT}/thin/server"
22
+ autoload :Stats, "#{ROOT}/thin/stats"
23
+
24
+ module Backends
25
+ autoload :Base, "#{ROOT}/thin/backends/base"
26
+ autoload :SwiftiplyClient, "#{ROOT}/thin/backends/swiftiply_client"
27
+ autoload :TcpServer, "#{ROOT}/thin/backends/tcp_server"
28
+ autoload :UnixServer, "#{ROOT}/thin/backends/unix_server"
29
+ end
30
+
31
+ module Controllers
32
+ autoload :Cluster, "#{ROOT}/thin/controllers/cluster"
33
+ autoload :Controller, "#{ROOT}/thin/controllers/controller"
34
+ autoload :Service, "#{ROOT}/thin/controllers/service"
35
+ end
36
+ end
37
+
38
+ require "#{Thin::ROOT}/thin/version"
39
+ require "#{Thin::ROOT}/thin/statuses"
40
+ require "#{Thin::ROOT}/rack/adapter/loader"
41
+
42
+ # support multiple Ruby version (fat binaries under windows)
43
+ begin
44
+ require "#{Thin::ROOT}/thin_parser"
45
+ rescue LoadError
46
+ if RUBY_PLATFORM =~ /mingw|mswin/ then
47
+ RUBY_VERSION =~ /(\d+.\d+)/
48
+ require "#{Thin::ROOT}/#{$1}/thin_parser"
49
+ end
50
+ end
51
+
52
+ module Rack
53
+ module Adapter
54
+ autoload :Rails, "#{Thin::ROOT}/rack/adapter/rails"
55
+ end
56
+ end
@@ -0,0 +1,149 @@
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
+ end
48
+
49
+ # Start the backend and connect it.
50
+ def start
51
+ @stopping = false
52
+ starter = proc do
53
+ connect
54
+ @running = true
55
+ end
56
+
57
+ # Allow for early run up of eventmachine.
58
+ if EventMachine.reactor_running?
59
+ starter.call
60
+ else
61
+ EventMachine.run(&starter)
62
+ end
63
+ end
64
+
65
+ # Stop of the backend from accepting new connections.
66
+ def stop
67
+ @running = false
68
+ @stopping = true
69
+
70
+ # Do not accept anymore connection
71
+ disconnect
72
+ stop! if @connections.empty?
73
+ end
74
+
75
+ # Force stop of the backend NOW, too bad for the current connections.
76
+ def stop!
77
+ @running = false
78
+ @stopping = false
79
+
80
+ EventMachine.stop if EventMachine.reactor_running?
81
+ @connections.each { |connection| connection.close_connection }
82
+ close
83
+ end
84
+
85
+ # Configure the backend. This method will be called before droping superuser privileges,
86
+ # so you can do crazy stuff that require godlike powers here.
87
+ def config
88
+ # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html
89
+ EventMachine.epoll unless @no_epoll
90
+
91
+ # Set the maximum number of socket descriptors that the server may open.
92
+ # The process needs to have required privilege to set it higher the 1024 on
93
+ # some systems.
94
+ @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win?
95
+ end
96
+
97
+ # Free up resources used by the backend.
98
+ def close
99
+ end
100
+
101
+ # Returns +true+ if the backend is connected and running.
102
+ def running?
103
+ @running
104
+ end
105
+
106
+ # Called by a connection when it's unbinded.
107
+ def connection_finished(connection)
108
+ @persistent_connection_count -= 1 if connection.can_persist?
109
+ @connections.delete(connection)
110
+
111
+ # Finalize gracefull stop if there's no more active connection.
112
+ stop! if @stopping && @connections.empty?
113
+ end
114
+
115
+ # Returns +true+ if no active connection.
116
+ def empty?
117
+ @connections.empty?
118
+ end
119
+
120
+ # Number of active connections.
121
+ def size
122
+ @connections.size
123
+ end
124
+
125
+ protected
126
+ # Initialize a new connection to a client.
127
+ def initialize_connection(connection)
128
+ connection.backend = self
129
+ connection.app = @server.app
130
+ connection.comm_inactivity_timeout = @timeout
131
+ connection.threaded = @threaded
132
+
133
+ if @ssl
134
+ connection.start_tls(@ssl_options)
135
+ end
136
+
137
+ # We control the number of persistent connections by keeping
138
+ # a count of the total one allowed yet.
139
+ if @persistent_connection_count < @maximum_persistent_connections
140
+ connection.can_persist!
141
+ @persistent_connection_count += 1
142
+ end
143
+
144
+ @connections << connection
145
+ end
146
+
147
+ end
148
+ end
149
+ end