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,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