puma 5.6.7 → 6.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +327 -16
  3. data/README.md +79 -29
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +1 -3
  7. data/docs/kubernetes.md +12 -0
  8. data/docs/nginx.md +1 -1
  9. data/docs/restart.md +1 -0
  10. data/docs/systemd.md +3 -6
  11. data/docs/testing_benchmarks_local_files.md +150 -0
  12. data/docs/testing_test_rackup_ci_files.md +36 -0
  13. data/ext/puma_http11/extconf.rb +16 -9
  14. data/ext/puma_http11/http11_parser.c +1 -1
  15. data/ext/puma_http11/http11_parser.h +1 -1
  16. data/ext/puma_http11/http11_parser.java.rl +2 -2
  17. data/ext/puma_http11/http11_parser.rl +2 -2
  18. data/ext/puma_http11/http11_parser_common.rl +2 -2
  19. data/ext/puma_http11/mini_ssl.c +127 -19
  20. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  21. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +157 -53
  23. data/ext/puma_http11/puma_http11.c +17 -9
  24. data/lib/puma/app/status.rb +4 -4
  25. data/lib/puma/binder.rb +50 -53
  26. data/lib/puma/cli.rb +16 -18
  27. data/lib/puma/client.rb +86 -19
  28. data/lib/puma/cluster/worker.rb +18 -11
  29. data/lib/puma/cluster/worker_handle.rb +4 -1
  30. data/lib/puma/cluster.rb +102 -40
  31. data/lib/puma/commonlogger.rb +21 -14
  32. data/lib/puma/configuration.rb +77 -59
  33. data/lib/puma/const.rb +129 -92
  34. data/lib/puma/control_cli.rb +15 -11
  35. data/lib/puma/detect.rb +7 -4
  36. data/lib/puma/dsl.rb +250 -56
  37. data/lib/puma/error_logger.rb +18 -9
  38. data/lib/puma/events.rb +6 -126
  39. data/lib/puma/io_buffer.rb +39 -4
  40. data/lib/puma/jruby_restart.rb +2 -1
  41. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  42. data/lib/puma/launcher.rb +102 -175
  43. data/lib/puma/log_writer.rb +147 -0
  44. data/lib/puma/minissl/context_builder.rb +26 -12
  45. data/lib/puma/minissl.rb +104 -11
  46. data/lib/puma/null_io.rb +16 -2
  47. data/lib/puma/plugin/systemd.rb +90 -0
  48. data/lib/puma/plugin/tmp_restart.rb +1 -1
  49. data/lib/puma/rack/builder.rb +6 -6
  50. data/lib/puma/rack/urlmap.rb +1 -1
  51. data/lib/puma/rack_default.rb +19 -4
  52. data/lib/puma/reactor.rb +19 -10
  53. data/lib/puma/request.rb +365 -170
  54. data/lib/puma/runner.rb +56 -20
  55. data/lib/puma/sd_notify.rb +149 -0
  56. data/lib/puma/server.rb +137 -89
  57. data/lib/puma/single.rb +13 -11
  58. data/lib/puma/state_file.rb +3 -6
  59. data/lib/puma/thread_pool.rb +57 -19
  60. data/lib/puma/util.rb +0 -11
  61. data/lib/puma.rb +9 -10
  62. data/lib/rack/handler/puma.rb +113 -86
  63. data/tools/Dockerfile +2 -2
  64. metadata +11 -7
  65. data/lib/puma/queue_close.rb +0 -26
  66. data/lib/puma/systemd.rb +0 -46
  67. data/lib/rack/version_restriction.rb +0 -15
@@ -36,13 +36,13 @@ static VALUE global_request_method;
36
36
  static VALUE global_request_uri;
37
37
  static VALUE global_fragment;
38
38
  static VALUE global_query_string;
39
- static VALUE global_http_version;
39
+ static VALUE global_server_protocol;
40
40
  static VALUE global_request_path;
41
41
 
42
42
  /** Defines common length and error messages for input length validation. */
43
43
  #define QUOTE(s) #s
44
- #define EXPLAIN_MAX_LENGTH_VALUE(s) QUOTE(s)
45
- #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
44
+ #define EXPAND_MAX_LENGTH_VALUE(s) QUOTE(s)
45
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPAND_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
46
46
 
47
47
  /** Validates the max length of given input and throws an HttpParserError exception if over. */
48
48
  #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
@@ -52,15 +52,23 @@ static VALUE global_request_path;
52
52
 
53
53
 
54
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_REQUEST_URI_MAX_LENGTH
56
+ #define PUMA_REQUEST_URI_MAX_LENGTH (1024 * 12)
57
+ #endif
58
+
59
+ #ifndef PUMA_REQUEST_PATH_MAX_LENGTH
60
+ #define PUMA_REQUEST_PATH_MAX_LENGTH (8192)
61
+ #endif
62
+
55
63
  #ifndef PUMA_QUERY_STRING_MAX_LENGTH
56
64
  #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
57
65
  #endif
58
66
 
59
67
  DEF_MAX_LENGTH(FIELD_NAME, 256);
60
68
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
61
- DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
69
+ DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
62
70
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
63
- DEF_MAX_LENGTH(REQUEST_PATH, 8192);
71
+ DEF_MAX_LENGTH(REQUEST_PATH, PUMA_REQUEST_PATH_MAX_LENGTH);
64
72
  DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
65
73
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
66
74
 
@@ -236,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
236
244
  rb_hash_aset(hp->request, global_query_string, val);
237
245
  }
238
246
 
239
- void http_version(puma_parser* hp, const char *at, size_t length)
247
+ void server_protocol(puma_parser* hp, const char *at, size_t length)
240
248
  {
241
249
  VALUE val = rb_str_new(at, length);
242
- rb_hash_aset(hp->request, global_http_version, val);
250
+ rb_hash_aset(hp->request, global_server_protocol, val);
243
251
  }
244
252
 
245
253
  /** Finalizes the request header to have a bunch of stuff that's
@@ -281,7 +289,7 @@ VALUE HttpParser_alloc(VALUE klass)
281
289
  hp->fragment = fragment;
282
290
  hp->request_path = request_path;
283
291
  hp->query_string = query_string;
284
- hp->http_version = http_version;
292
+ hp->server_protocol = server_protocol;
285
293
  hp->header_done = header_done;
286
294
  hp->request = Qnil;
287
295
 
@@ -461,7 +469,7 @@ void Init_puma_http11(void)
461
469
  DEF_GLOBAL(request_uri, "REQUEST_URI");
462
470
  DEF_GLOBAL(fragment, "FRAGMENT");
463
471
  DEF_GLOBAL(query_string, "QUERY_STRING");
464
- DEF_GLOBAL(http_version, "HTTP_VERSION");
472
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
465
473
  DEF_GLOBAL(request_path, "REQUEST_PATH");
466
474
 
467
475
  eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'puma/json_serialization'
2
+ require_relative '../json_serialization'
3
3
 
4
4
  module Puma
5
5
  module App
@@ -80,13 +80,13 @@ module Puma
80
80
 
81
81
  def authenticate(env)
82
82
  return true unless @auth_token
83
- env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
83
+ env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
84
84
  end
85
85
 
86
86
  def rack_response(status, body, content_type='application/json')
87
87
  headers = {
88
- 'Content-Type' => content_type,
89
- 'Content-Length' => body.bytesize.to_s
88
+ 'content-type' => content_type,
89
+ 'content-length' => body.bytesize.to_s
90
90
  }
91
91
 
92
92
  [status, headers, [body]]
data/lib/puma/binder.rb CHANGED
@@ -3,24 +3,15 @@
3
3
  require 'uri'
4
4
  require 'socket'
5
5
 
6
- require 'puma/const'
7
- require 'puma/util'
8
- require 'puma/configuration'
6
+ require_relative 'const'
7
+ require_relative 'util'
8
+ require_relative 'configuration'
9
9
 
10
10
  module Puma
11
11
 
12
12
  if HAS_SSL
13
- require 'puma/minissl'
14
- require 'puma/minissl/context_builder'
15
-
16
- # Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
17
- # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
- # The bug was that it did not require openssl.
19
- # @todo remove when Ruby 2.3 support is dropped
20
- #
21
- if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
22
- require 'openssl'
23
- end
13
+ require_relative 'minissl'
14
+ require_relative 'minissl/context_builder'
24
15
  end
25
16
 
26
17
  class Binder
@@ -28,8 +19,8 @@ module Puma
28
19
 
29
20
  RACK_VERSION = [1,6].freeze
30
21
 
31
- def initialize(events, conf = Configuration.new)
32
- @events = events
22
+ def initialize(log_writer, conf = Configuration.new)
23
+ @log_writer = log_writer
33
24
  @conf = conf
34
25
  @listeners = []
35
26
  @inherited_fds = {}
@@ -38,7 +29,7 @@ module Puma
38
29
 
39
30
  @proto_env = {
40
31
  "rack.version".freeze => RACK_VERSION,
41
- "rack.errors".freeze => events.stderr,
32
+ "rack.errors".freeze => log_writer.stderr,
42
33
  "rack.multithread".freeze => conf.options[:max_threads] > 1,
43
34
  "rack.multiprocess".freeze => conf.options[:workers] >= 1,
44
35
  "rack.run_once".freeze => false,
@@ -51,14 +42,12 @@ module Puma
51
42
  # infer properly.
52
43
 
53
44
  "QUERY_STRING".freeze => "",
54
- SERVER_PROTOCOL => HTTP_11,
55
45
  SERVER_SOFTWARE => PUMA_SERVER_STRING,
56
46
  GATEWAY_INTERFACE => CGI_VER
57
47
  }
58
48
 
59
49
  @envs = {}
60
50
  @ios = []
61
- localhost_authority
62
51
  end
63
52
 
64
53
  attr_reader :ios
@@ -80,7 +69,7 @@ module Puma
80
69
  # @!attribute [r] connected_ports
81
70
  # @version 5.0.0
82
71
  def connected_ports
83
- ios.map { |io| io.addr[1] }.uniq
72
+ t = ios.map { |io| io.addr[1] }; t.uniq!; t
84
73
  end
85
74
 
86
75
  # @version 5.0.0
@@ -98,7 +87,7 @@ module Puma
98
87
  # @version 5.0.0
99
88
  #
100
89
  def create_activated_fds(env_hash)
101
- @events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
90
+ @log_writer.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
102
91
  return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
103
92
  env_hash['LISTEN_FDS'].to_i.times do |index|
104
93
  sock = TCPServer.for_fd(socket_activation_fd(index))
@@ -106,11 +95,11 @@ module Puma
106
95
  [:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
107
96
  rescue ArgumentError # Try to parse as a port/ip
108
97
  port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
109
- addr = "[#{addr}]" if addr =~ /\:/
98
+ addr = "[#{addr}]" if addr&.include? ':'
110
99
  [:tcp, addr, port]
111
100
  end
112
101
  @activated_sockets[key] = sock
113
- @events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
102
+ @log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
114
103
  end
115
104
  ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
116
105
  end
@@ -152,29 +141,30 @@ module Puma
152
141
  end
153
142
  end
154
143
 
155
- def parse(binds, logger, log_msg = 'Listening')
144
+ def parse(binds, log_writer = nil, log_msg = 'Listening')
145
+ log_writer ||= @log_writer
156
146
  binds.each do |str|
157
147
  uri = URI.parse str
158
148
  case uri.scheme
159
149
  when "tcp"
160
150
  if fd = @inherited_fds.delete(str)
161
151
  io = inherit_tcp_listener uri.host, uri.port, fd
162
- logger.log "* Inherited #{str}"
152
+ log_writer.log "* Inherited #{str}"
163
153
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
164
154
  io = inherit_tcp_listener uri.host, uri.port, sock
165
- logger.log "* Activated #{str}"
155
+ log_writer.log "* Activated #{str}"
166
156
  else
167
157
  ios_len = @ios.length
168
158
  params = Util.parse_query uri.query
169
159
 
170
- opt = params.key?('low_latency') && params['low_latency'] != 'false'
160
+ low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
171
161
  backlog = params.fetch('backlog', 1024).to_i
172
162
 
173
- io = add_tcp_listener uri.host, uri.port, opt, backlog
163
+ io = add_tcp_listener uri.host, uri.port, low_latency, backlog
174
164
 
175
165
  @ios[ios_len..-1].each do |i|
176
166
  addr = loc_addr_str i
177
- logger.log "* #{log_msg} on http://#{addr}"
167
+ log_writer.log "* #{log_msg} on http://#{addr}"
178
168
  end
179
169
  end
180
170
 
@@ -191,12 +181,12 @@ module Puma
191
181
  if fd = @inherited_fds.delete(str)
192
182
  @unix_paths << path unless abstract || File.exist?(path)
193
183
  io = inherit_unix_listener path, fd
194
- logger.log "* Inherited #{str}"
184
+ log_writer.log "* Inherited #{str}"
195
185
  elsif sock = @activated_sockets.delete([ :unix, path ]) ||
196
186
  @activated_sockets.delete([ :unix, File.realdirpath(path) ])
197
187
  @unix_paths << path unless abstract || File.exist?(path)
198
188
  io = inherit_unix_listener path, sock
199
- logger.log "* Activated #{str}"
189
+ log_writer.log "* Activated #{str}"
200
190
  else
201
191
  umask = nil
202
192
  mode = nil
@@ -220,11 +210,12 @@ module Puma
220
210
 
221
211
  @unix_paths << path unless abstract || File.exist?(path)
222
212
  io = add_unix_listener path, umask, mode, backlog
223
- logger.log "* #{log_msg} on #{str}"
213
+ log_writer.log "* #{log_msg} on #{str}"
224
214
  end
225
215
 
226
216
  @listeners << [str, io]
227
217
  when "ssl"
218
+ cert_key = %w[cert key]
228
219
 
229
220
  raise "Puma compiled without SSL support" unless HAS_SSL
230
221
 
@@ -233,49 +224,51 @@ module Puma
233
224
  # If key and certs are not defined and localhost gem is required.
234
225
  # localhost gem will be used for self signed
235
226
  # Load localhost authority if not loaded.
236
- if params.values_at('cert', 'key').all? { |v| v.to_s.empty? }
227
+ # Ruby 3 `values_at` accepts an array, earlier do not
228
+ if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
237
229
  ctx = localhost_authority && localhost_authority_context
238
230
  end
239
231
 
240
232
  ctx ||=
241
233
  begin
242
234
  # Extract cert_pem and key_pem from options[:store] if present
243
- ['cert', 'key'].each do |v|
244
- if params[v] && params[v].start_with?('store:')
235
+ cert_key.each do |v|
236
+ if params[v]&.start_with?('store:')
245
237
  index = Integer(params.delete(v).split('store:').last)
246
238
  params["#{v}_pem"] = @conf.options[:store][index]
247
239
  end
248
240
  end
249
- MiniSSL::ContextBuilder.new(params, @events).context
241
+ MiniSSL::ContextBuilder.new(params, @log_writer).context
250
242
  end
251
243
 
252
244
  if fd = @inherited_fds.delete(str)
253
- logger.log "* Inherited #{str}"
245
+ log_writer.log "* Inherited #{str}"
254
246
  io = inherit_ssl_listener fd, ctx
255
247
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
256
248
  io = inherit_ssl_listener sock, ctx
257
- logger.log "* Activated #{str}"
249
+ log_writer.log "* Activated #{str}"
258
250
  else
259
251
  ios_len = @ios.length
260
252
  backlog = params.fetch('backlog', 1024).to_i
261
- io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
253
+ low_latency = params['low_latency'] != 'false'
254
+ io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
262
255
 
263
256
  @ios[ios_len..-1].each do |i|
264
257
  addr = loc_addr_str i
265
- logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
258
+ log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
266
259
  end
267
260
  end
268
261
 
269
262
  @listeners << [str, io] if io
270
263
  else
271
- logger.error "Invalid URI: #{str}"
264
+ log_writer.error "Invalid URI: #{str}"
272
265
  end
273
266
  end
274
267
 
275
268
  # If we inherited fds but didn't use them (because of a
276
269
  # configuration change), then be sure to close them.
277
270
  @inherited_fds.each do |str, fd|
278
- logger.log "* Closing unused inherited connection: #{str}"
271
+ log_writer.log "* Closing unused inherited connection: #{str}"
279
272
 
280
273
  begin
281
274
  IO.for_fd(fd).close
@@ -295,7 +288,7 @@ module Puma
295
288
  fds = @ios.map(&:to_i)
296
289
  @activated_sockets.each do |key, sock|
297
290
  next if fds.include? sock.to_i
298
- logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
291
+ log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
299
292
  begin
300
293
  sock.close
301
294
  rescue SystemCallError
@@ -319,7 +312,7 @@ module Puma
319
312
  local_certificates_path = File.expand_path("~/.localhost")
320
313
  [File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
321
314
  end
322
- MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
315
+ MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
323
316
  end
324
317
 
325
318
  # Tell the server to listen on host +host+, port +port+.
@@ -337,7 +330,7 @@ module Puma
337
330
  return
338
331
  end
339
332
 
340
- host = host[1..-2] if host and host[0..0] == '['
333
+ host = host[1..-2] if host&.start_with? '['
341
334
  tcp_server = TCPServer.new(host, port)
342
335
 
343
336
  if optimize_for_latency
@@ -371,7 +364,7 @@ module Puma
371
364
  return
372
365
  end
373
366
 
374
- host = host[1..-2] if host[0..0] == '['
367
+ host = host[1..-2] if host&.start_with? '['
375
368
  s = TCPServer.new(host, port)
376
369
  if optimize_for_latency
377
370
  s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
@@ -457,11 +450,14 @@ module Puma
457
450
 
458
451
  def close_listeners
459
452
  @listeners.each do |l, io|
460
- io.close unless io.closed?
461
- uri = URI.parse l
462
- next unless uri.scheme == 'unix'
463
- unix_path = "#{uri.host}#{uri.path}"
464
- File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
453
+ begin
454
+ io.close unless io.closed?
455
+ uri = URI.parse l
456
+ next unless uri.scheme == 'unix'
457
+ unix_path = "#{uri.host}#{uri.path}"
458
+ File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
459
+ rescue Errno::EBADF
460
+ end
465
461
  end
466
462
  end
467
463
 
@@ -482,9 +478,10 @@ module Puma
482
478
 
483
479
  # @!attribute [r] loopback_addresses
484
480
  def loopback_addresses
485
- Socket.ip_address_list.select do |addrinfo|
481
+ t = Socket.ip_address_list.select do |addrinfo|
486
482
  addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
487
- end.map { |addrinfo| addrinfo.ip_address }.uniq
483
+ end
484
+ t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
488
485
  end
489
486
 
490
487
  def loc_addr_str(io)
data/lib/puma/cli.rb CHANGED
@@ -3,11 +3,11 @@
3
3
  require 'optparse'
4
4
  require 'uri'
5
5
 
6
- require 'puma'
7
- require 'puma/configuration'
8
- require 'puma/launcher'
9
- require 'puma/const'
10
- require 'puma/events'
6
+ require_relative '../puma'
7
+ require_relative 'configuration'
8
+ require_relative 'launcher'
9
+ require_relative 'const'
10
+ require_relative 'log_writer'
11
11
 
12
12
  module Puma
13
13
  class << self
@@ -21,19 +21,13 @@ module Puma
21
21
  # Handles invoke a Puma::Server in a command line style.
22
22
  #
23
23
  class CLI
24
- # @deprecated 6.0.0
25
- KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
26
-
27
24
  # Create a new CLI object using +argv+ as the command line
28
25
  # arguments.
29
26
  #
30
- # +stdout+ and +stderr+ can be set to IO-like objects which
31
- # this object will report status on.
32
- #
33
- def initialize(argv, events=Events.stdio)
27
+ def initialize(argv, log_writer = LogWriter.stdio, events = Events.new)
34
28
  @debug = false
35
29
  @argv = argv.dup
36
-
30
+ @log_writer = log_writer
37
31
  @events = events
38
32
 
39
33
  @conf = nil
@@ -69,7 +63,7 @@ module Puma
69
63
  end
70
64
  end
71
65
 
72
- @launcher = Puma::Launcher.new(@conf, :events => @events, :argv => argv)
66
+ @launcher = Puma::Launcher.new(@conf, :log_writer => @log_writer, :events => @events, :argv => argv)
73
67
  end
74
68
 
75
69
  attr_reader :launcher
@@ -83,7 +77,7 @@ module Puma
83
77
 
84
78
  private
85
79
  def unsupported(str)
86
- @events.error(str)
80
+ @log_writer.error(str)
87
81
  raise UnsupportedOption
88
82
  end
89
83
 
@@ -99,7 +93,7 @@ module Puma
99
93
  #
100
94
 
101
95
  def setup_options
102
- @conf = Configuration.new do |user_config, file_config|
96
+ @conf = Configuration.new({}, {events: @events}) do |user_config, file_config|
103
97
  @parser = OptionParser.new do |o|
104
98
  o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
105
99
  user_config.bind arg
@@ -150,9 +144,13 @@ module Puma
150
144
  $LOAD_PATH.unshift(*arg.split(':'))
151
145
  end
152
146
 
147
+ o.on "--idle-timeout SECONDS", "Number of seconds until the next request before automatic shutdown" do |arg|
148
+ user_config.idle_timeout arg
149
+ end
150
+
153
151
  o.on "-p", "--port PORT", "Define the TCP port to bind to",
154
152
  "Use -b for more advanced options" do |arg|
155
- user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
153
+ user_config.bind "tcp://#{Configuration::DEFAULTS[:tcp_host]}:#{arg}"
156
154
  end
157
155
 
158
156
  o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
@@ -186,7 +184,7 @@ module Puma
186
184
  end
187
185
 
188
186
  o.on "-s", "--silent", "Do not log prompt messages other than errors" do
189
- @events = Events.new NullIO.new, $stderr
187
+ @log_writer = LogWriter.new(NullIO.new, $stderr)
190
188
  end
191
189
 
192
190
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|