gross 1.7.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 (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,238 @@
1
+ require 'logger'
2
+ require 'optparse'
3
+ require 'yaml'
4
+ require 'erb'
5
+
6
+ module Thin
7
+ # CLI runner.
8
+ # Parse options and send command to the correct Controller.
9
+ class Runner
10
+ COMMANDS = %w(start stop restart config)
11
+ LINUX_ONLY_COMMANDS = %w(install)
12
+
13
+ # Commands that wont load options from the config file
14
+ CONFIGLESS_COMMANDS = %w(config install)
15
+
16
+ # Parsed options
17
+ attr_accessor :options
18
+
19
+ # Name of the command to be runned.
20
+ attr_accessor :command
21
+
22
+ # Arguments to be passed to the command.
23
+ attr_accessor :arguments
24
+
25
+ # Return all available commands
26
+ def self.commands
27
+ commands = COMMANDS
28
+ commands += LINUX_ONLY_COMMANDS if Thin.linux?
29
+ commands
30
+ end
31
+
32
+ def initialize(argv)
33
+ @argv = argv
34
+
35
+ # Default options values
36
+ @options = {
37
+ :chdir => Dir.pwd,
38
+ :environment => ENV['RACK_ENV'] || 'development',
39
+ :address => '0.0.0.0',
40
+ :port => Server::DEFAULT_PORT,
41
+ :timeout => Server::DEFAULT_TIMEOUT,
42
+ :log => File.join(Dir.pwd, 'log/thin.log'),
43
+ :pid => 'tmp/pids/thin.pid',
44
+ :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS,
45
+ :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
46
+ :require => [],
47
+ :wait => Controllers::Cluster::DEFAULT_WAIT_TIME,
48
+ :threadpool_size => 20
49
+ }
50
+
51
+ parse!
52
+ end
53
+
54
+ def parser
55
+ # NOTE: If you add an option here make sure the key in the +options+ hash is the
56
+ # same as the name of the command line option.
57
+ # +option+ keys are used to build the command line to launch other processes,
58
+ # see <tt>lib/thin/command.rb</tt>.
59
+ @parser ||= OptionParser.new do |opts|
60
+ opts.banner = "Usage: thin [options] #{self.class.commands.join('|')}"
61
+
62
+ opts.separator ""
63
+ opts.separator "Server options:"
64
+
65
+ opts.on("-a", "--address HOST", "bind to HOST address " +
66
+ "(default: #{@options[:address]})") { |host| @options[:address] = host }
67
+ opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i }
68
+ opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file }
69
+ opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key }
70
+ opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)",
71
+ "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name }
72
+ opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " +
73
+ "Rack adapter") { |file| @options[:rackup] = file }
74
+ opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) }
75
+ opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| @options[:stats] = path }
76
+
77
+ opts.separator ""
78
+ opts.separator "SSL options:"
79
+
80
+ opts.on( "--ssl", "Enables SSL") { @options[:ssl] = true }
81
+ opts.on( "--ssl-key-file PATH", "Path to private key") { |path| @options[:ssl_key_file] = path }
82
+ opts.on( "--ssl-cert-file PATH", "Path to certificate") { |path| @options[:ssl_cert_file] = path }
83
+ opts.on( "--ssl-disable-verify", "Disables (optional) client cert requests") { @options[:ssl_disable_verify] = true }
84
+ opts.on( "--ssl-version VERSION", "TLSv1, TLSv1_1, TLSv1_2") { |version| @options[:ssl_version] = version }
85
+ opts.on( "--ssl-cipher-list STRING", "Example: HIGH:!ADH:!RC4:-MEDIUM:-LOW:-EXP:-CAMELLIA") { |cipher| @options[:ssl_cipher_list] = cipher }
86
+
87
+ opts.separator ""
88
+ opts.separator "Adapter options:"
89
+ opts.on("-e", "--environment ENV", "Framework environment " +
90
+ "(default: #{@options[:environment]})") { |env| @options[:environment] = env }
91
+ opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| @options[:prefix] = path }
92
+
93
+ unless Thin.win? # Daemonizing not supported on Windows
94
+ opts.separator ""
95
+ opts.separator "Daemon options:"
96
+
97
+ opts.on("-d", "--daemonize", "Run daemonized in the background") { @options[:daemonize] = true }
98
+ opts.on("-l", "--log FILE", "File to redirect output " +
99
+ "(default: #{@options[:log]})") { |file| @options[:log] = file }
100
+ opts.on("-P", "--pid FILE", "File to store PID " +
101
+ "(default: #{@options[:pid]})") { |file| @options[:pid] = file }
102
+ opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| @options[:user] = user }
103
+ opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| @options[:group] = group }
104
+ opts.on( "--tag NAME", "Additional text to display in process listing") { |tag| @options[:tag] = tag }
105
+
106
+ opts.separator ""
107
+ opts.separator "Cluster options:"
108
+
109
+ opts.on("-s", "--servers NUM", "Number of servers to start") { |num| @options[:servers] = num.to_i }
110
+ opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i }
111
+ opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file }
112
+ opts.on( "--all [DIR]", "Send command to each config files in DIR") { |dir| @options[:all] = dir } if Thin.linux?
113
+ opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true }
114
+ opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i }
115
+ end
116
+
117
+ opts.separator ""
118
+ opts.separator "Tuning options:"
119
+
120
+ opts.on("-b", "--backend CLASS", "Backend to use, full classname") { |name| @options[:backend] = name }
121
+ opts.on("-t", "--timeout SEC", "Request or command timeout in sec " +
122
+ "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i }
123
+ opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true }
124
+ opts.on( "--max-conns NUM", "Maximum number of open file descriptors " +
125
+ "(default: #{@options[:max_conns]})",
126
+ "Might require sudo to set higher than 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win?
127
+ opts.on( "--max-persistent-conns NUM",
128
+ "Maximum number of persistent connections",
129
+ "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i }
130
+ opts.on( "--threaded", "Call the Rack application in threads " +
131
+ "[experimental]") { @options[:threaded] = true }
132
+ opts.on( "--threadpool-size NUM", "Sets the size of the EventMachine threadpool.",
133
+ "(default: #{@options[:threadpool_size]})") { |num| @options[:threadpool_size] = num.to_i }
134
+ opts.on( "--no-epoll", "Disable the use of epoll") { @options[:no_epoll] = true } if Thin.linux?
135
+
136
+ opts.separator ""
137
+ opts.separator "Common options:"
138
+
139
+ opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file }
140
+ opts.on_tail("-q", "--quiet", "Silence all logging") { @options[:quiet] = true }
141
+ opts.on_tail("-D", "--debug", "Enable debug logging") { @options[:debug] = true }
142
+ opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true }
143
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
144
+ opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit }
145
+ end
146
+ end
147
+
148
+ # Parse the options.
149
+ def parse!
150
+ parser.parse! @argv
151
+ @command = @argv.shift
152
+ @arguments = @argv
153
+ end
154
+
155
+ # Parse the current shell arguments and run the command.
156
+ # Exits on error.
157
+ def run!
158
+ if self.class.commands.include?(@command)
159
+ run_command
160
+ elsif @command.nil?
161
+ puts "Command required"
162
+ puts @parser
163
+ exit 1
164
+ else
165
+ abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}"
166
+ end
167
+ end
168
+
169
+ # Send the command to the controller: single instance or cluster.
170
+ def run_command
171
+ load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command)
172
+
173
+ # PROGRAM_NAME is relative to the current directory, so make sure
174
+ # we store and expand it before changing directory.
175
+ Command.script = File.expand_path($PROGRAM_NAME)
176
+
177
+ # Change the current directory ASAP so that all relative paths are
178
+ # relative to this one.
179
+ Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command)
180
+
181
+ @options[:require].each { |r| ruby_require r }
182
+
183
+ # Setup the logger
184
+ if @options[:quiet]
185
+ Logging.silent = true
186
+ else
187
+ Logging.level = Logger::DEBUG if @options[:debug]
188
+ end
189
+
190
+ if @options[:trace]
191
+ # Trace raw requests/responses
192
+ Logging.trace_logger = Logging.logger
193
+ end
194
+
195
+ controller = case
196
+ when cluster? then Controllers::Cluster.new(@options)
197
+ when service? then Controllers::Service.new(@options)
198
+ else Controllers::Controller.new(@options)
199
+ end
200
+
201
+ if controller.respond_to?(@command)
202
+ begin
203
+ controller.send(@command, *@arguments)
204
+ rescue RunnerError => e
205
+ abort e.message
206
+ end
207
+ else
208
+ abort "Invalid options for command: #{@command}"
209
+ end
210
+ end
211
+
212
+ # +true+ if we're controlling a cluster.
213
+ def cluster?
214
+ @options[:only] || @options[:servers] || @options[:config]
215
+ end
216
+
217
+ # +true+ if we're acting a as system service.
218
+ def service?
219
+ @options.has_key?(:all) || @command == 'install'
220
+ end
221
+
222
+ private
223
+ def load_options_from_config_file!
224
+ if file = @options.delete(:config)
225
+ YAML.load(ERB.new(File.read(file)).result).each { |key, value| @options[key.to_sym] = value }
226
+ end
227
+ end
228
+
229
+ def ruby_require(file)
230
+ if File.extname(file) == '.ru'
231
+ warn 'WARNING: Use the -R option to load a Rack config file'
232
+ @options[:rackup] = file
233
+ else
234
+ require file
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,290 @@
1
+ module Thin
2
+ # The utterly famous Thin HTTP server.
3
+ # It listens for incoming requests through a given +backend+
4
+ # and forwards all requests to +app+.
5
+ #
6
+ # == TCP server
7
+ # Create a new TCP server bound to <tt>host:port</tt> by specifiying +host+
8
+ # and +port+ as the first 2 arguments.
9
+ #
10
+ # Thin::Server.start('0.0.0.0', 3000, app)
11
+ #
12
+ # == UNIX domain server
13
+ # Create a new UNIX domain socket bound to +socket+ file by specifiying a filename
14
+ # as the first argument. Eg.: /tmp/thin.sock. If the first argument contains a <tt>/</tt>
15
+ # it will be assumed to be a UNIX socket.
16
+ #
17
+ # Thin::Server.start('/tmp/thin.sock', app)
18
+ #
19
+ # == Using a custom backend
20
+ # You can implement your own way to connect the server to its client by creating your
21
+ # own Backend class and passing it as the :backend option.
22
+ #
23
+ # Thin::Server.start('galaxy://faraway', 1345, app, :backend => Thin::Backends::MyFancyBackend)
24
+ #
25
+ # == Rack application (+app+)
26
+ # All requests will be processed through +app+, which must be a valid Rack adapter.
27
+ # A valid Rack adapter (application) must respond to <tt>call(env#Hash)</tt> and
28
+ # return an array of <tt>[status, headers, body]</tt>.
29
+ #
30
+ # == Building an app in place
31
+ # If a block is passed, a <tt>Rack::Builder</tt> instance
32
+ # will be passed to build the +app+. So you can do cool stuff like this:
33
+ #
34
+ # Thin::Server.start('0.0.0.0', 3000) do
35
+ # use Rack::CommonLogger
36
+ # use Rack::ShowExceptions
37
+ # map "/lobster" do
38
+ # use Rack::Lint
39
+ # run Rack::Lobster.new
40
+ # end
41
+ # end
42
+ #
43
+ # == Controlling with signals
44
+ # * INT and TERM: Force shutdown (see Server#stop!)
45
+ # * TERM & QUIT calls +stop+ to shutdown gracefully.
46
+ # * HUP calls +restart+ to ... surprise, restart!
47
+ # * USR1 reopen log files.
48
+ # Signals are processed at one second intervals.
49
+ # Disable signals by passing <tt>:signals => false</tt>.
50
+ #
51
+ class Server
52
+ include Logging
53
+ include Daemonizable
54
+ extend Forwardable
55
+
56
+ # Default values
57
+ DEFAULT_TIMEOUT = 30 #sec
58
+ DEFAULT_HOST = '0.0.0.0'
59
+ DEFAULT_PORT = 3000
60
+ DEFAULT_MAXIMUM_CONNECTIONS = 1024
61
+ DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS = 100
62
+
63
+ # Application (Rack adapter) called with the request that produces the response.
64
+ attr_accessor :app
65
+
66
+ # A tag that will show in the process listing
67
+ attr_accessor :tag
68
+
69
+ # Backend handling the connections to the clients.
70
+ attr_accessor :backend
71
+
72
+ # Maximum number of seconds for incoming data to arrive before the connection
73
+ # is dropped.
74
+ def_delegators :backend, :timeout, :timeout=
75
+
76
+ # Maximum number of file or socket descriptors that the server may open.
77
+ def_delegators :backend, :maximum_connections, :maximum_connections=
78
+
79
+ # Maximum number of connections that can be persistent at the same time.
80
+ # Most browsers never close the connection so most of the time they are closed
81
+ # when the timeout occurs. If we don't control the number of persistent connections,
82
+ # it would be very easy to overflow the server for a DoS attack.
83
+ def_delegators :backend, :maximum_persistent_connections, :maximum_persistent_connections=
84
+
85
+ # Allow using threads in the backend.
86
+ def_delegators :backend, :threaded?, :threaded=, :threadpool_size, :threadpool_size=
87
+
88
+ # Allow using SSL in the backend.
89
+ def_delegators :backend, :ssl?, :ssl=, :ssl_options=
90
+
91
+ # Address and port on which the server is listening for connections.
92
+ def_delegators :backend, :host, :port
93
+
94
+ # UNIX domain socket on which the server is listening for connections.
95
+ def_delegator :backend, :socket
96
+
97
+ # Disable the use of epoll under Linux
98
+ def_delegators :backend, :no_epoll, :no_epoll=
99
+
100
+ def initialize(*args, &block)
101
+ host, port, options = DEFAULT_HOST, DEFAULT_PORT, {}
102
+
103
+ # Guess each parameter by its type so they can be
104
+ # received in any order.
105
+ args.each do |arg|
106
+ case arg
107
+ when 0.class, /^\d+$/ then port = arg.to_i
108
+ when String then host = arg
109
+ when Hash then options = arg
110
+ else
111
+ @app = arg if arg.respond_to?(:call)
112
+ end
113
+ end
114
+
115
+ # Set tag if needed
116
+ self.tag = options[:tag]
117
+
118
+ # Try to intelligently select which backend to use.
119
+ @backend = select_backend(host, port, options)
120
+
121
+ load_cgi_multipart_eof_fix
122
+
123
+ @backend.server = self
124
+
125
+ # Set defaults
126
+ @backend.maximum_connections = DEFAULT_MAXIMUM_CONNECTIONS
127
+ @backend.maximum_persistent_connections = DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS
128
+ @backend.timeout = options[:timeout] || DEFAULT_TIMEOUT
129
+
130
+ # Allow using Rack builder as a block
131
+ @app = Rack::Builder.new(&block).to_app if block
132
+
133
+ # If in debug mode, wrap in logger adapter
134
+ @app = Rack::CommonLogger.new(@app) if Logging.debug?
135
+
136
+ @setup_signals = options[:signals] != false
137
+ end
138
+
139
+ # Lil' shortcut to turn this:
140
+ #
141
+ # Server.new(...).start
142
+ #
143
+ # into this:
144
+ #
145
+ # Server.start(...)
146
+ #
147
+ def self.start(*args, &block)
148
+ new(*args, &block).start!
149
+ end
150
+
151
+ # Start the server and listen for connections.
152
+ def start
153
+ raise ArgumentError, 'app required' unless @app
154
+
155
+ log_info "Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})"
156
+ log_debug "Debugging ON"
157
+ trace "Tracing ON"
158
+
159
+ log_info "Maximum connections set to #{@backend.maximum_connections}"
160
+ log_info "Listening on #{@backend}, CTRL+C to stop"
161
+
162
+ @backend.start { setup_signals if @setup_signals }
163
+ end
164
+ alias :start! :start
165
+
166
+ # == Gracefull shutdown
167
+ # Stops the server after processing all current connections.
168
+ # As soon as this method is called, the server stops accepting
169
+ # new requests and waits for all current connections to finish.
170
+ # Calling twice is the equivalent of calling <tt>stop!</tt>.
171
+ def stop
172
+ if running?
173
+ @backend.stop
174
+ unless @backend.empty?
175
+ log_info "Waiting for #{@backend.size} connection(s) to finish, "\
176
+ "can take up to #{timeout} sec, CTRL+C to stop now"
177
+ end
178
+ else
179
+ stop!
180
+ end
181
+ end
182
+
183
+ # == Force shutdown
184
+ # Stops the server closing all current connections right away.
185
+ # This doesn't wait for connection to finish their work and send data.
186
+ # All current requests will be dropped.
187
+ def stop!
188
+ if @backend.started_reactor?
189
+ log_info "Stopping ..."
190
+ else
191
+ log_info "Stopping Thin ..."
192
+ log_info "Thin was started inside an existing EventMachine.run block."
193
+ log_info "Call `EventMachine.stop` to stop the reactor and quit the process."
194
+ end
195
+
196
+ @backend.stop!
197
+ end
198
+
199
+ # == Reopen log file.
200
+ # Reopen the log file and redirect STDOUT and STDERR to it.
201
+ def reopen_log
202
+ return unless log_file
203
+ file = File.expand_path(log_file)
204
+ log_info "Reopening log file: #{file}"
205
+ Daemonize.redirect_io(file)
206
+ end
207
+
208
+ # == Configure the server
209
+ # The process might need to have superuser privilege to configure
210
+ # server with optimal options.
211
+ def config
212
+ @backend.config
213
+ end
214
+
215
+ # Name of the server and type of backend used.
216
+ # This is also the name of the process in which Thin is running as a daemon.
217
+ def name
218
+ "thin server (#{@backend})" + (tag ? " [#{tag}]" : "")
219
+ end
220
+ alias :to_s :name
221
+
222
+ # Return +true+ if the server is running and ready to receive requests.
223
+ # Note that the server might still be running and return +false+ when
224
+ # shuting down and waiting for active connections to complete.
225
+ def running?
226
+ @backend.running?
227
+ end
228
+
229
+ protected
230
+ def setup_signals
231
+ # Queue up signals so they are processed in non-trap context
232
+ # using a EM timer.
233
+ @signal_queue ||= []
234
+
235
+ %w( INT TERM ).each do |signal|
236
+ trap(signal) { @signal_queue.push signal }
237
+ end
238
+ # *nix only signals
239
+ %w( QUIT HUP USR1 ).each do |signal|
240
+ trap(signal) { @signal_queue.push signal }
241
+ end unless Thin.win?
242
+
243
+ # Signals are processed at one second intervals.
244
+ @signal_timer ||= EM.add_periodic_timer(1) { handle_signals }
245
+ end
246
+
247
+ def handle_signals
248
+ case @signal_queue.shift
249
+ when 'INT'
250
+ stop!
251
+ when 'TERM', 'QUIT'
252
+ stop
253
+ when 'HUP'
254
+ restart
255
+ when 'USR1'
256
+ reopen_log
257
+ end
258
+ EM.next_tick { handle_signals } unless @signal_queue.empty?
259
+ end
260
+
261
+ def select_backend(host, port, options)
262
+ case
263
+ when options.has_key?(:backend)
264
+ raise ArgumentError, ":backend must be a class" unless options[:backend].is_a?(Class)
265
+ options[:backend].new(host, port, options)
266
+ when options.has_key?(:swiftiply)
267
+ Backends::SwiftiplyClient.new(host, port, options)
268
+ when host.include?('/')
269
+ Backends::UnixServer.new(host)
270
+ else
271
+ Backends::TcpServer.new(host, port)
272
+ end
273
+ end
274
+
275
+ # Taken from Mongrel cgi_multipart_eof_fix
276
+ # Ruby 1.8.5 has a security bug in cgi.rb, we need to patch it.
277
+ def load_cgi_multipart_eof_fix
278
+ version = RUBY_VERSION.split('.').map { |i| i.to_i }
279
+
280
+ if version[0] <= 1 && version[1] <= 8 && version[2] <= 5 && RUBY_PLATFORM !~ /java/
281
+ begin
282
+ require 'cgi_multipart_eof_fix'
283
+ rescue LoadError
284
+ log_error "Ruby 1.8.5 is not secure please install cgi_multipart_eof_fix:"
285
+ log_error "gem install cgi_multipart_eof_fix"
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end