puma 3.12.0 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1413 -439
  3. data/LICENSE +23 -20
  4. data/README.md +131 -60
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +24 -19
  7. data/docs/compile_options.md +19 -0
  8. data/docs/deployment.md +38 -13
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +1 -1
  16. data/docs/plugins.md +20 -10
  17. data/docs/rails_dev_mode.md +29 -0
  18. data/docs/restart.md +47 -22
  19. data/docs/signals.md +7 -6
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +48 -70
  22. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  23. data/ext/puma_http11/ext_help.h +1 -1
  24. data/ext/puma_http11/extconf.rb +27 -0
  25. data/ext/puma_http11/http11_parser.c +84 -109
  26. data/ext/puma_http11/http11_parser.h +1 -1
  27. data/ext/puma_http11/http11_parser.java.rl +22 -38
  28. data/ext/puma_http11/http11_parser.rl +4 -2
  29. data/ext/puma_http11/http11_parser_common.rl +3 -3
  30. data/ext/puma_http11/mini_ssl.c +262 -87
  31. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  32. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  33. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
  34. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
  35. data/ext/puma_http11/puma_http11.c +34 -50
  36. data/lib/puma/app/status.rb +68 -49
  37. data/lib/puma/binder.rb +197 -144
  38. data/lib/puma/cli.rb +17 -15
  39. data/lib/puma/client.rb +257 -226
  40. data/lib/puma/cluster/worker.rb +176 -0
  41. data/lib/puma/cluster/worker_handle.rb +90 -0
  42. data/lib/puma/cluster.rb +223 -212
  43. data/lib/puma/commonlogger.rb +4 -2
  44. data/lib/puma/configuration.rb +58 -51
  45. data/lib/puma/const.rb +41 -19
  46. data/lib/puma/control_cli.rb +117 -73
  47. data/lib/puma/detect.rb +26 -3
  48. data/lib/puma/dsl.rb +531 -123
  49. data/lib/puma/error_logger.rb +104 -0
  50. data/lib/puma/events.rb +57 -31
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +2 -58
  53. data/lib/puma/json.rb +96 -0
  54. data/lib/puma/launcher.rb +182 -70
  55. data/lib/puma/minissl/context_builder.rb +79 -0
  56. data/lib/puma/minissl.rb +149 -48
  57. data/lib/puma/null_io.rb +15 -1
  58. data/lib/puma/plugin/tmp_restart.rb +2 -0
  59. data/lib/puma/plugin.rb +8 -12
  60. data/lib/puma/queue_close.rb +26 -0
  61. data/lib/puma/rack/builder.rb +4 -5
  62. data/lib/puma/rack/urlmap.rb +2 -0
  63. data/lib/puma/rack_default.rb +2 -0
  64. data/lib/puma/reactor.rb +87 -316
  65. data/lib/puma/request.rb +456 -0
  66. data/lib/puma/runner.rb +33 -52
  67. data/lib/puma/server.rb +288 -679
  68. data/lib/puma/single.rb +13 -67
  69. data/lib/puma/state_file.rb +10 -3
  70. data/lib/puma/systemd.rb +46 -0
  71. data/lib/puma/thread_pool.rb +131 -81
  72. data/lib/puma/util.rb +14 -6
  73. data/lib/puma.rb +54 -0
  74. data/lib/rack/handler/puma.rb +8 -6
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +45 -29
  78. data/ext/puma_http11/io_buffer.c +0 -155
  79. data/lib/puma/accept_nonblock.rb +0 -23
  80. data/lib/puma/compat.rb +0 -14
  81. data/lib/puma/convenient.rb +0 -23
  82. data/lib/puma/daemon_ext.rb +0 -31
  83. data/lib/puma/delegation.rb +0 -11
  84. data/lib/puma/java_io_buffer.rb +0 -45
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -39
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
@@ -0,0 +1,456 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+
5
+ # The methods here are included in Server, but are separated into this file.
6
+ # All the methods here pertain to passing the request to the app, then
7
+ # writing the response back to the client.
8
+ #
9
+ # None of the methods here are called externally, with the exception of
10
+ # #handle_request, which is called in Server#process_client.
11
+ # @version 5.0.3
12
+ #
13
+ module Request
14
+
15
+ include Puma::Const
16
+
17
+ # Takes the request contained in +client+, invokes the Rack application to construct
18
+ # the response and writes it back to +client.io+.
19
+ #
20
+ # It'll return +false+ when the connection is closed, this doesn't mean
21
+ # that the response wasn't successful.
22
+ #
23
+ # It'll return +:async+ if the connection remains open but will be handled
24
+ # elsewhere, i.e. the connection has been hijacked by the Rack application.
25
+ #
26
+ # Finally, it'll return +true+ on keep-alive connections.
27
+ # @param client [Puma::Client]
28
+ # @param lines [Puma::IOBuffer]
29
+ # @return [Boolean,:async]
30
+ #
31
+ def handle_request(client, lines)
32
+ env = client.env
33
+ io = client.io # io may be a MiniSSL::Socket
34
+
35
+ return false if closed_socket?(io)
36
+
37
+ normalize_env env, client
38
+
39
+ env[PUMA_SOCKET] = io
40
+
41
+ if env[HTTPS_KEY] && io.peercert
42
+ env[PUMA_PEERCERT] = io.peercert
43
+ end
44
+
45
+ env[HIJACK_P] = true
46
+ env[HIJACK] = client
47
+
48
+ body = client.body
49
+
50
+ head = env[REQUEST_METHOD] == HEAD
51
+
52
+ env[RACK_INPUT] = body
53
+ env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
54
+
55
+ if @early_hints
56
+ env[EARLY_HINTS] = lambda { |headers|
57
+ begin
58
+ fast_write io, str_early_hints(headers)
59
+ rescue ConnectionError => e
60
+ @events.debug_error e
61
+ # noop, if we lost the socket we just won't send the early hints
62
+ end
63
+ }
64
+ end
65
+
66
+ req_env_post_parse env
67
+
68
+ # A rack extension. If the app writes #call'ables to this
69
+ # array, we will invoke them when the request is done.
70
+ #
71
+ after_reply = env[RACK_AFTER_REPLY] = []
72
+
73
+ begin
74
+ begin
75
+ status, headers, res_body = @thread_pool.with_force_shutdown do
76
+ @app.call(env)
77
+ end
78
+
79
+ return :async if client.hijacked
80
+
81
+ status = status.to_i
82
+
83
+ if status == -1
84
+ unless headers.empty? and res_body == []
85
+ raise "async response must have empty headers and body"
86
+ end
87
+
88
+ return :async
89
+ end
90
+ rescue ThreadPool::ForceShutdown => e
91
+ @events.unknown_error e, client, "Rack app"
92
+ @events.log "Detected force shutdown of a thread"
93
+
94
+ status, headers, res_body = lowlevel_error(e, env, 503)
95
+ rescue Exception => e
96
+ @events.unknown_error e, client, "Rack app"
97
+
98
+ status, headers, res_body = lowlevel_error(e, env, 500)
99
+ end
100
+
101
+ res_info = {}
102
+ res_info[:content_length] = nil
103
+ res_info[:no_body] = head
104
+
105
+ res_info[:content_length] = if res_body.kind_of? Array and res_body.size == 1
106
+ res_body[0].bytesize
107
+ else
108
+ nil
109
+ end
110
+
111
+ cork_socket io
112
+
113
+ str_headers(env, status, headers, res_info, lines)
114
+
115
+ line_ending = LINE_END
116
+
117
+ content_length = res_info[:content_length]
118
+ response_hijack = res_info[:response_hijack]
119
+
120
+ if res_info[:no_body]
121
+ if content_length and status != 204
122
+ lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
123
+ end
124
+
125
+ lines << LINE_END
126
+ fast_write io, lines.to_s
127
+ return res_info[:keep_alive]
128
+ end
129
+
130
+ if content_length
131
+ lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
132
+ chunked = false
133
+ elsif !response_hijack and res_info[:allow_chunked]
134
+ lines << TRANSFER_ENCODING_CHUNKED
135
+ chunked = true
136
+ end
137
+
138
+ lines << line_ending
139
+
140
+ fast_write io, lines.to_s
141
+
142
+ if response_hijack
143
+ response_hijack.call io
144
+ return :async
145
+ end
146
+
147
+ begin
148
+ res_body.each do |part|
149
+ next if part.bytesize.zero?
150
+ if chunked
151
+ fast_write io, (part.bytesize.to_s(16) << line_ending)
152
+ fast_write io, part # part may have different encoding
153
+ fast_write io, line_ending
154
+ else
155
+ fast_write io, part
156
+ end
157
+ io.flush
158
+ end
159
+
160
+ if chunked
161
+ fast_write io, CLOSE_CHUNKED
162
+ io.flush
163
+ end
164
+ rescue SystemCallError, IOError
165
+ raise ConnectionError, "Connection error detected during write"
166
+ end
167
+
168
+ ensure
169
+ uncork_socket io
170
+
171
+ body.close
172
+ client.tempfile.unlink if client.tempfile
173
+ res_body.close if res_body.respond_to? :close
174
+
175
+ after_reply.each { |o| o.call }
176
+ end
177
+
178
+ return res_info[:keep_alive]
179
+ end
180
+
181
+ # @param env [Hash] see Puma::Client#env, from request
182
+ # @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
183
+ #
184
+ def default_server_port(env)
185
+ if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
186
+ PORT_443
187
+ else
188
+ PORT_80
189
+ end
190
+ end
191
+
192
+ # Writes to an io (normally Client#io) using #syswrite
193
+ # @param io [#syswrite] the io to write to
194
+ # @param str [String] the string written to the io
195
+ # @raise [ConnectionError]
196
+ #
197
+ def fast_write(io, str)
198
+ n = 0
199
+ while true
200
+ begin
201
+ n = io.syswrite str
202
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
203
+ if !IO.select(nil, [io], nil, WRITE_TIMEOUT)
204
+ raise ConnectionError, "Socket timeout writing data"
205
+ end
206
+
207
+ retry
208
+ rescue Errno::EPIPE, SystemCallError, IOError
209
+ raise ConnectionError, "Socket timeout writing data"
210
+ end
211
+
212
+ return if n == str.bytesize
213
+ str = str.byteslice(n..-1)
214
+ end
215
+ end
216
+ private :fast_write
217
+
218
+ # @param status [Integer] status from the app
219
+ # @return [String] the text description from Puma::HTTP_STATUS_CODES
220
+ #
221
+ def fetch_status_code(status)
222
+ HTTP_STATUS_CODES.fetch(status) { 'CUSTOM' }
223
+ end
224
+ private :fetch_status_code
225
+
226
+ # Given a Hash +env+ for the request read from +client+, add
227
+ # and fixup keys to comply with Rack's env guidelines.
228
+ # @param env [Hash] see Puma::Client#env, from request
229
+ # @param client [Puma::Client] only needed for Client#peerip
230
+ # @todo make private in 6.0.0
231
+ #
232
+ def normalize_env(env, client)
233
+ if host = env[HTTP_HOST]
234
+ # host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port.
235
+ if colon = host.rindex("]:") # IPV6 with port
236
+ env[SERVER_NAME] = host[0, colon+1]
237
+ env[SERVER_PORT] = host[colon+2, host.bytesize]
238
+ elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port
239
+ env[SERVER_NAME] = host[0, colon]
240
+ env[SERVER_PORT] = host[colon+1, host.bytesize]
241
+ else
242
+ env[SERVER_NAME] = host
243
+ env[SERVER_PORT] = default_server_port(env)
244
+ end
245
+ else
246
+ env[SERVER_NAME] = LOCALHOST
247
+ env[SERVER_PORT] = default_server_port(env)
248
+ end
249
+
250
+ unless env[REQUEST_PATH]
251
+ # it might be a dumbass full host request header
252
+ uri = URI.parse(env[REQUEST_URI])
253
+ env[REQUEST_PATH] = uri.path
254
+
255
+ raise "No REQUEST PATH" unless env[REQUEST_PATH]
256
+
257
+ # A nil env value will cause a LintError (and fatal errors elsewhere),
258
+ # so only set the env value if there actually is a value.
259
+ env[QUERY_STRING] = uri.query if uri.query
260
+ end
261
+
262
+ env[PATH_INFO] = env[REQUEST_PATH]
263
+
264
+ # From https://www.ietf.org/rfc/rfc3875 :
265
+ # "Script authors should be aware that the REMOTE_ADDR and
266
+ # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
267
+ # may not identify the ultimate source of the request.
268
+ # They identify the client for the immediate request to the
269
+ # server; that client may be a proxy, gateway, or other
270
+ # intermediary acting on behalf of the actual source client."
271
+ #
272
+
273
+ unless env.key?(REMOTE_ADDR)
274
+ begin
275
+ addr = client.peerip
276
+ rescue Errno::ENOTCONN
277
+ # Client disconnects can result in an inability to get the
278
+ # peeraddr from the socket; default to localhost.
279
+ addr = LOCALHOST_IP
280
+ end
281
+
282
+ # Set unix socket addrs to localhost
283
+ addr = LOCALHOST_IP if addr.empty?
284
+
285
+ env[REMOTE_ADDR] = addr
286
+ end
287
+ end
288
+ # private :normalize_env
289
+
290
+ # @param header_key [#to_s]
291
+ # @return [Boolean]
292
+ #
293
+ def illegal_header_key?(header_key)
294
+ !!(ILLEGAL_HEADER_KEY_REGEX =~ header_key.to_s)
295
+ end
296
+
297
+ # @param header_value [#to_s]
298
+ # @return [Boolean]
299
+ #
300
+ def illegal_header_value?(header_value)
301
+ !!(ILLEGAL_HEADER_VALUE_REGEX =~ header_value.to_s)
302
+ end
303
+ private :illegal_header_key?, :illegal_header_value?
304
+
305
+ # Fixup any headers with `,` in the name to have `_` now. We emit
306
+ # headers with `,` in them during the parse phase to avoid ambiguity
307
+ # with the `-` to `_` conversion for critical headers. But here for
308
+ # compatibility, we'll convert them back. This code is written to
309
+ # avoid allocation in the common case (ie there are no headers
310
+ # with `,` in their names), that's why it has the extra conditionals.
311
+ # @param env [Hash] see Puma::Client#env, from request, modifies in place
312
+ # @version 5.0.3
313
+ #
314
+ def req_env_post_parse(env)
315
+ to_delete = nil
316
+ to_add = nil
317
+
318
+ env.each do |k,v|
319
+ if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
320
+ if to_delete
321
+ to_delete << k
322
+ else
323
+ to_delete = [k]
324
+ end
325
+
326
+ unless to_add
327
+ to_add = {}
328
+ end
329
+
330
+ to_add[k.tr(",", "_")] = v
331
+ end
332
+ end
333
+
334
+ if to_delete
335
+ to_delete.each { |k| env.delete(k) }
336
+ env.merge! to_add
337
+ end
338
+ end
339
+ private :req_env_post_parse
340
+
341
+ # Used in the lambda for env[ `Puma::Const::EARLY_HINTS` ]
342
+ # @param headers [Hash] the headers returned by the Rack application
343
+ # @return [String]
344
+ # @version 5.0.3
345
+ #
346
+ def str_early_hints(headers)
347
+ eh_str = "HTTP/1.1 103 Early Hints\r\n".dup
348
+ headers.each_pair do |k, vs|
349
+ next if illegal_header_key?(k)
350
+
351
+ if vs.respond_to?(:to_s) && !vs.to_s.empty?
352
+ vs.to_s.split(NEWLINE).each do |v|
353
+ next if illegal_header_value?(v)
354
+ eh_str << "#{k}: #{v}\r\n"
355
+ end
356
+ else
357
+ eh_str << "#{k}: #{vs}\r\n"
358
+ end
359
+ end
360
+ "#{eh_str}\r\n".freeze
361
+ end
362
+ private :str_early_hints
363
+
364
+ # Processes and write headers to the IOBuffer.
365
+ # @param env [Hash] see Puma::Client#env, from request
366
+ # @param status [Integer] the status returned by the Rack application
367
+ # @param headers [Hash] the headers returned by the Rack application
368
+ # @param res_info [Hash] used to pass info between this method and #handle_request
369
+ # @param lines [Puma::IOBuffer] modified inn place
370
+ # @version 5.0.3
371
+ #
372
+ def str_headers(env, status, headers, res_info, lines)
373
+ line_ending = LINE_END
374
+ colon = COLON
375
+
376
+ http_11 = env[HTTP_VERSION] == HTTP_11
377
+ if http_11
378
+ res_info[:allow_chunked] = true
379
+ res_info[:keep_alive] = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
380
+
381
+ # An optimization. The most common response is 200, so we can
382
+ # reply with the proper 200 status without having to compute
383
+ # the response header.
384
+ #
385
+ if status == 200
386
+ lines << HTTP_11_200
387
+ else
388
+ lines.append "HTTP/1.1 ", status.to_s, " ",
389
+ fetch_status_code(status), line_ending
390
+
391
+ res_info[:no_body] ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
392
+ end
393
+ else
394
+ res_info[:allow_chunked] = false
395
+ res_info[:keep_alive] = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
396
+
397
+ # Same optimization as above for HTTP/1.1
398
+ #
399
+ if status == 200
400
+ lines << HTTP_10_200
401
+ else
402
+ lines.append "HTTP/1.0 ", status.to_s, " ",
403
+ fetch_status_code(status), line_ending
404
+
405
+ res_info[:no_body] ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
406
+ end
407
+ end
408
+
409
+ # regardless of what the client wants, we always close the connection
410
+ # if running without request queueing
411
+ res_info[:keep_alive] &&= @queue_requests
412
+
413
+ res_info[:response_hijack] = nil
414
+
415
+ headers.each do |k, vs|
416
+ next if illegal_header_key?(k)
417
+
418
+ case k.downcase
419
+ when CONTENT_LENGTH2
420
+ next if illegal_header_value?(vs)
421
+ res_info[:content_length] = vs
422
+ next
423
+ when TRANSFER_ENCODING
424
+ res_info[:allow_chunked] = false
425
+ res_info[:content_length] = nil
426
+ when HIJACK
427
+ res_info[:response_hijack] = vs
428
+ next
429
+ when BANNED_HEADER_KEY
430
+ next
431
+ end
432
+
433
+ if vs.respond_to?(:to_s) && !vs.to_s.empty?
434
+ vs.to_s.split(NEWLINE).each do |v|
435
+ next if illegal_header_value?(v)
436
+ lines.append k, colon, v, line_ending
437
+ end
438
+ else
439
+ lines.append k, colon, line_ending
440
+ end
441
+ end
442
+
443
+ # HTTP/1.1 & 1.0 assume different defaults:
444
+ # - HTTP 1.0 assumes the connection will be closed if not specified
445
+ # - HTTP 1.1 assumes the connection will be kept alive if not specified.
446
+ # Only set the header if we're doing something which is not the default
447
+ # for this protocol version
448
+ if http_11
449
+ lines << CONNECTION_CLOSE if !res_info[:keep_alive]
450
+ else
451
+ lines << CONNECTION_KEEP_ALIVE if res_info[:keep_alive]
452
+ end
453
+ end
454
+ private :str_headers
455
+ end
456
+ end
data/lib/puma/runner.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/server'
2
4
  require 'puma/const'
3
5
 
@@ -12,10 +14,7 @@ module Puma
12
14
  @options = cli.options
13
15
  @app = nil
14
16
  @control = nil
15
- end
16
-
17
- def daemon?
18
- @options[:daemon]
17
+ @started_at = Time.now
19
18
  end
20
19
 
21
20
  def development?
@@ -30,7 +29,8 @@ module Puma
30
29
  @events.log str
31
30
  end
32
31
 
33
- def before_restart
32
+ # @version 5.0.0
33
+ def stop_control
34
34
  @control.stop(true) if @control
35
35
  end
36
36
 
@@ -48,36 +48,27 @@ module Puma
48
48
 
49
49
  require 'puma/app/status'
50
50
 
51
- uri = URI.parse str
52
-
53
- app = Puma::App::Status.new @launcher
54
-
55
51
  if token = @options[:control_auth_token]
56
- app.auth_token = token unless token.empty? or token == :none
52
+ token = nil if token.empty? || token == 'none'
57
53
  end
58
54
 
59
- control = Puma::Server.new app, @launcher.events
60
- control.min_threads = 0
61
- control.max_threads = 1
55
+ app = Puma::App::Status.new @launcher, token
62
56
 
63
- case uri.scheme
64
- when "tcp"
65
- log "* Starting control server on #{str}"
66
- control.add_tcp_listener uri.host, uri.port
67
- when "unix"
68
- log "* Starting control server on #{str}"
69
- path = "#{uri.host}#{uri.path}"
70
- mask = @options[:control_url_umask]
57
+ control = Puma::Server.new app, @launcher.events,
58
+ { min_threads: 0, max_threads: 1, queue_requests: false }
71
59
 
72
- control.add_unix_listener path, mask
73
- else
74
- error "Invalid control URI: #{str}"
75
- end
60
+ control.binder.parse [str], self, 'Starting control server'
76
61
 
77
- control.run
62
+ control.run thread_name: 'control'
78
63
  @control = control
79
64
  end
80
65
 
66
+ # @version 5.0.0
67
+ def close_control_listeners
68
+ @control.binder.close_listeners if @control
69
+ end
70
+
71
+ # @!attribute [r] ruby_engine
81
72
  def ruby_engine
82
73
  if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
83
74
  "ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
@@ -95,12 +86,15 @@ module Puma
95
86
  max_t = @options[:max_threads]
96
87
 
97
88
  log "Puma starting in #{mode} mode..."
98
- log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
99
- log "* Min threads: #{min_t}, max threads: #{max_t}"
100
- log "* Environment: #{ENV['RACK_ENV']}"
89
+ log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
90
+ log "* Min threads: #{min_t}"
91
+ log "* Max threads: #{max_t}"
92
+ log "* Environment: #{ENV['RACK_ENV']}"
101
93
 
102
- if @options[:mode] == :tcp
103
- log "* Mode: Lopez Express (tcp)"
94
+ if mode == "cluster"
95
+ log "* Master PID: #{Process.pid}"
96
+ else
97
+ log "* PID: #{Process.pid}"
104
98
  end
105
99
  end
106
100
 
@@ -119,8 +113,8 @@ module Puma
119
113
  end
120
114
 
121
115
  STDOUT.reopen stdout, (append ? "a" : "w")
122
- STDOUT.sync = true
123
116
  STDOUT.puts "=== puma startup: #{Time.now} ==="
117
+ STDOUT.flush unless STDOUT.sync
124
118
  end
125
119
 
126
120
  if stderr
@@ -129,8 +123,13 @@ module Puma
129
123
  end
130
124
 
131
125
  STDERR.reopen stderr, (append ? "a" : "w")
132
- STDERR.sync = true
133
126
  STDERR.puts "=== puma startup: #{Time.now} ==="
127
+ STDERR.flush unless STDERR.sync
128
+ end
129
+
130
+ if @options[:mutate_stdout_and_stderr_to_sync_on_write]
131
+ STDOUT.sync = true
132
+ STDERR.sync = true
134
133
  end
135
134
  end
136
135
 
@@ -140,7 +139,6 @@ module Puma
140
139
  exit 1
141
140
  end
142
141
 
143
- # Load the app before we daemonize.
144
142
  begin
145
143
  @app = @launcher.config.app
146
144
  rescue Exception => e
@@ -151,31 +149,14 @@ module Puma
151
149
  @launcher.binder.parse @options[:binds], self
152
150
  end
153
151
 
152
+ # @!attribute [r] app
154
153
  def app
155
154
  @app ||= @launcher.config.app
156
155
  end
157
156
 
158
157
  def start_server
159
- min_t = @options[:min_threads]
160
- max_t = @options[:max_threads]
161
-
162
158
  server = Puma::Server.new app, @launcher.events, @options
163
- server.min_threads = min_t
164
- server.max_threads = max_t
165
159
  server.inherit_binder @launcher.binder
166
-
167
- if @options[:mode] == :tcp
168
- server.tcp_mode!
169
- end
170
-
171
- if @options[:early_hints]
172
- server.early_hints = true
173
- end
174
-
175
- unless development? || test?
176
- server.leak_stack_on_error = false
177
- end
178
-
179
160
  server
180
161
  end
181
162
  end