puma 4.1.1 → 5.0.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +149 -10
  3. data/LICENSE +23 -20
  4. data/README.md +30 -46
  5. data/docs/architecture.md +3 -3
  6. data/docs/deployment.md +9 -3
  7. data/docs/fork_worker.md +31 -0
  8. data/docs/jungle/README.md +13 -0
  9. data/{tools → docs}/jungle/rc.d/README.md +0 -0
  10. data/{tools → docs}/jungle/rc.d/puma +0 -0
  11. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  12. data/{tools → docs}/jungle/upstart/README.md +0 -0
  13. data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
  14. data/{tools → docs}/jungle/upstart/puma.conf +0 -0
  15. data/docs/plugins.md +20 -10
  16. data/docs/signals.md +7 -6
  17. data/docs/systemd.md +1 -63
  18. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  19. data/ext/puma_http11/extconf.rb +6 -0
  20. data/ext/puma_http11/http11_parser.c +40 -63
  21. data/ext/puma_http11/http11_parser.java.rl +21 -37
  22. data/ext/puma_http11/http11_parser.rl +3 -1
  23. data/ext/puma_http11/http11_parser_common.rl +3 -3
  24. data/ext/puma_http11/mini_ssl.c +15 -2
  25. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  26. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  27. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
  28. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
  29. data/ext/puma_http11/puma_http11.c +9 -38
  30. data/lib/puma.rb +23 -0
  31. data/lib/puma/app/status.rb +46 -30
  32. data/lib/puma/binder.rb +112 -124
  33. data/lib/puma/cli.rb +11 -15
  34. data/lib/puma/client.rb +250 -209
  35. data/lib/puma/cluster.rb +203 -85
  36. data/lib/puma/commonlogger.rb +2 -2
  37. data/lib/puma/configuration.rb +31 -42
  38. data/lib/puma/const.rb +24 -19
  39. data/lib/puma/control_cli.rb +46 -17
  40. data/lib/puma/detect.rb +17 -0
  41. data/lib/puma/dsl.rb +162 -70
  42. data/lib/puma/error_logger.rb +97 -0
  43. data/lib/puma/events.rb +35 -31
  44. data/lib/puma/io_buffer.rb +9 -2
  45. data/lib/puma/jruby_restart.rb +0 -58
  46. data/lib/puma/launcher.rb +117 -58
  47. data/lib/puma/minissl.rb +60 -18
  48. data/lib/puma/minissl/context_builder.rb +73 -0
  49. data/lib/puma/null_io.rb +1 -1
  50. data/lib/puma/plugin.rb +6 -12
  51. data/lib/puma/rack/builder.rb +0 -4
  52. data/lib/puma/reactor.rb +16 -9
  53. data/lib/puma/runner.rb +11 -32
  54. data/lib/puma/server.rb +173 -193
  55. data/lib/puma/single.rb +7 -64
  56. data/lib/puma/state_file.rb +6 -3
  57. data/lib/puma/thread_pool.rb +104 -81
  58. data/lib/rack/handler/puma.rb +1 -5
  59. data/tools/Dockerfile +16 -0
  60. data/tools/trickletest.rb +0 -1
  61. metadata +23 -24
  62. data/ext/puma_http11/io_buffer.c +0 -155
  63. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  64. data/lib/puma/convenient.rb +0 -25
  65. data/lib/puma/daemon_ext.rb +0 -33
  66. data/lib/puma/delegation.rb +0 -13
  67. data/lib/puma/tcp_logger.rb +0 -41
  68. data/tools/jungle/README.md +0 -19
  69. data/tools/jungle/init.d/README.md +0 -61
  70. data/tools/jungle/init.d/puma +0 -421
  71. data/tools/jungle/init.d/run-puma +0 -18
@@ -9,13 +9,11 @@ require 'puma/null_io'
9
9
  require 'puma/reactor'
10
10
  require 'puma/client'
11
11
  require 'puma/binder'
12
- require 'puma/delegation'
13
- require 'puma/accept_nonblock'
14
12
  require 'puma/util'
15
-
16
- require 'puma/puma_http11'
13
+ require 'puma/io_buffer'
17
14
 
18
15
  require 'socket'
16
+ require 'forwardable'
19
17
 
20
18
  module Puma
21
19
 
@@ -32,10 +30,11 @@ module Puma
32
30
  class Server
33
31
 
34
32
  include Puma::Const
35
- extend Puma::Delegation
33
+ extend Forwardable
36
34
 
37
35
  attr_reader :thread
38
36
  attr_reader :events
37
+ attr_reader :requests_count # @version 5.0.0
39
38
  attr_accessor :app
40
39
 
41
40
  attr_accessor :min_threads
@@ -57,8 +56,7 @@ module Puma
57
56
  @app = app
58
57
  @events = events
59
58
 
60
- @check, @notify = Puma::Util.pipe
61
-
59
+ @check, @notify = nil
62
60
  @status = :stop
63
61
 
64
62
  @min_threads = 0
@@ -85,27 +83,34 @@ module Puma
85
83
  @mode = :http
86
84
 
87
85
  @precheck_closing = true
86
+
87
+ @requests_count = 0
88
88
  end
89
89
 
90
90
  attr_accessor :binder, :leak_stack_on_error, :early_hints
91
91
 
92
- forward :add_tcp_listener, :@binder
93
- forward :add_ssl_listener, :@binder
94
- forward :add_unix_listener, :@binder
95
- forward :connected_port, :@binder
92
+ def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_ports
96
93
 
97
94
  def inherit_binder(bind)
98
95
  @binder = bind
99
96
  end
100
97
 
101
- def tcp_mode!
102
- @mode = :tcp
98
+ class << self
99
+ # :nodoc:
100
+ # @version 5.0.0
101
+ def tcp_cork_supported?
102
+ RbConfig::CONFIG['host_os'] =~ /linux/ &&
103
+ Socket.const_defined?(:IPPROTO_TCP) &&
104
+ Socket.const_defined?(:TCP_CORK) &&
105
+ Socket.const_defined?(:TCP_INFO)
106
+ end
107
+ private :tcp_cork_supported?
103
108
  end
104
109
 
105
110
  # On Linux, use TCP_CORK to better control how the TCP stack
106
111
  # packetizes our stream. This improves both latency and throughput.
107
112
  #
108
- if RUBY_PLATFORM =~ /linux/
113
+ if tcp_cork_supported?
109
114
  UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
110
115
 
111
116
  # 6 == Socket::IPPROTO_TCP
@@ -113,7 +118,7 @@ module Puma
113
118
  # 1/0 == turn on/off
114
119
  def cork_socket(socket)
115
120
  begin
116
- socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
121
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
117
122
  rescue IOError, SystemCallError
118
123
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
119
124
  end
@@ -121,7 +126,7 @@ module Puma
121
126
 
122
127
  def uncork_socket(socket)
123
128
  begin
124
- socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
129
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
125
130
  rescue IOError, SystemCallError
126
131
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
127
132
  end
@@ -132,7 +137,7 @@ module Puma
132
137
  return false unless @precheck_closing
133
138
 
134
139
  begin
135
- tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
140
+ tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
136
141
  rescue IOError, SystemCallError
137
142
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
138
143
  @precheck_closing = false
@@ -175,104 +180,6 @@ module Puma
175
180
  @thread_pool and @thread_pool.pool_capacity
176
181
  end
177
182
 
178
- # Lopez Mode == raw tcp apps
179
-
180
- def run_lopez_mode(background=true)
181
- @thread_pool = ThreadPool.new(@min_threads,
182
- @max_threads,
183
- Hash) do |client, tl|
184
-
185
- io = client.to_io
186
- addr = io.peeraddr.last
187
-
188
- if addr.empty?
189
- # Set unix socket addrs to localhost
190
- addr = "127.0.0.1:0"
191
- else
192
- addr = "#{addr}:#{io.peeraddr[1]}"
193
- end
194
-
195
- env = { 'thread' => tl, REMOTE_ADDR => addr }
196
-
197
- begin
198
- @app.call env, client.to_io
199
- rescue Object => e
200
- STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
201
- STDERR.puts e.backtrace
202
- end
203
-
204
- client.close unless env['detach']
205
- end
206
-
207
- @events.fire :state, :running
208
-
209
- if background
210
- @thread = Thread.new { handle_servers_lopez_mode }
211
- return @thread
212
- else
213
- handle_servers_lopez_mode
214
- end
215
- end
216
-
217
- def handle_servers_lopez_mode
218
- begin
219
- check = @check
220
- sockets = [check] + @binder.ios
221
- pool = @thread_pool
222
-
223
- while @status == :run
224
- begin
225
- ios = IO.select sockets
226
- ios.first.each do |sock|
227
- if sock == check
228
- break if handle_check
229
- else
230
- begin
231
- if io = sock.accept_nonblock
232
- client = Client.new io, nil
233
- pool << client
234
- end
235
- rescue SystemCallError
236
- # nothing
237
- rescue Errno::ECONNABORTED
238
- # client closed the socket even before accept
239
- begin
240
- io.close
241
- rescue
242
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
243
- end
244
- end
245
- end
246
- end
247
- rescue Object => e
248
- @events.unknown_error self, e, "Listen loop"
249
- end
250
- end
251
-
252
- @events.fire :state, @status
253
-
254
- graceful_shutdown if @status == :stop || @status == :restart
255
-
256
- rescue Exception => e
257
- STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
258
- STDERR.puts e.backtrace
259
- ensure
260
- begin
261
- @check.close
262
- rescue
263
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
264
- end
265
-
266
- # Prevent can't modify frozen IOError (RuntimeError)
267
- begin
268
- @notify.close
269
- rescue IOError
270
- # no biggy
271
- end
272
- end
273
-
274
- @events.fire :state, :done
275
- end
276
183
  # Runs the server.
277
184
  #
278
185
  # If +background+ is true (the default) then a thread is spun
@@ -286,15 +193,9 @@ module Puma
286
193
 
287
194
  @status = :run
288
195
 
289
- if @mode == :tcp
290
- return run_lopez_mode(background)
291
- end
292
-
293
- queue_requests = @queue_requests
294
-
295
196
  @thread_pool = ThreadPool.new(@min_threads,
296
197
  @max_threads,
297
- IOBuffer) do |client, buffer|
198
+ ::Puma::IOBuffer) do |client, buffer|
298
199
 
299
200
  # Advertise this server into the thread
300
201
  Thread.current[ThreadLocalKey] = self
@@ -302,10 +203,10 @@ module Puma
302
203
  process_now = false
303
204
 
304
205
  begin
305
- if queue_requests
206
+ if @queue_requests
306
207
  process_now = client.eagerly_finish
307
208
  else
308
- client.finish
209
+ client.finish(@first_data_timeout)
309
210
  process_now = true
310
211
  end
311
212
  rescue MiniSSL::SSLError => e
@@ -315,14 +216,16 @@ module Puma
315
216
 
316
217
  client.close
317
218
 
318
- @events.ssl_error self, addr, cert, e
219
+ @events.ssl_error e, addr, cert
319
220
  rescue HttpParserError => e
320
- client.write_400
221
+ client.write_error(400)
321
222
  client.close
322
223
 
323
- @events.parse_error self, client.env, e
324
- rescue ConnectionError, EOFError
224
+ @events.parse_error e, client
225
+ rescue ConnectionError, EOFError => e
325
226
  client.close
227
+
228
+ @events.connection_error e, client
326
229
  else
327
230
  if process_now
328
231
  process_client client, buffer
@@ -331,11 +234,14 @@ module Puma
331
234
  @reactor.add client
332
235
  end
333
236
  end
237
+
238
+ process_now
334
239
  end
335
240
 
241
+ @thread_pool.out_of_band_hook = @options[:out_of_band]
336
242
  @thread_pool.clean_thread_locals = @options[:clean_thread_locals]
337
243
 
338
- if queue_requests
244
+ if @queue_requests
339
245
  @reactor = Reactor.new self, @thread_pool
340
246
  @reactor.run_in_thread
341
247
  end
@@ -351,7 +257,10 @@ module Puma
351
257
  @events.fire :state, :running
352
258
 
353
259
  if background
354
- @thread = Thread.new { handle_servers }
260
+ @thread = Thread.new do
261
+ Puma.set_thread_name "server"
262
+ handle_servers
263
+ end
355
264
  return @thread
356
265
  else
357
266
  handle_servers
@@ -359,6 +268,7 @@ module Puma
359
268
  end
360
269
 
361
270
  def handle_servers
271
+ @check, @notify = Puma::Util.pipe unless @notify
362
272
  begin
363
273
  check = @check
364
274
  sockets = [check] + @binder.ios
@@ -382,51 +292,49 @@ module Puma
382
292
  if sock == check
383
293
  break if handle_check
384
294
  else
385
- begin
386
- if io = sock.accept_nonblock
387
- client = Client.new io, @binder.env(sock)
388
- if remote_addr_value
389
- client.peerip = remote_addr_value
390
- elsif remote_addr_header
391
- client.remote_addr_header = remote_addr_header
392
- end
393
-
394
- pool << client
395
- busy_threads = pool.wait_until_not_full
396
- if busy_threads == 0
397
- @options[:out_of_band].each(&:call) if @options[:out_of_band]
398
- end
399
- end
400
- rescue SystemCallError
401
- # nothing
402
- rescue Errno::ECONNABORTED
403
- # client closed the socket even before accept
404
- begin
405
- io.close
406
- rescue
407
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
408
- end
295
+ pool.wait_until_not_full
296
+ pool.wait_for_less_busy_worker(
297
+ @options[:wait_for_less_busy_worker].to_f)
298
+
299
+ io = begin
300
+ sock.accept_nonblock
301
+ rescue IO::WaitReadable
302
+ next
303
+ end
304
+ client = Client.new io, @binder.env(sock)
305
+ if remote_addr_value
306
+ client.peerip = remote_addr_value
307
+ elsif remote_addr_header
308
+ client.remote_addr_header = remote_addr_header
409
309
  end
310
+ pool << client
410
311
  end
411
312
  end
412
313
  rescue Object => e
413
- @events.unknown_error self, e, "Listen loop"
314
+ @events.unknown_error e, nil, "Listen loop"
414
315
  end
415
316
  end
416
317
 
417
318
  @events.fire :state, @status
418
319
 
419
- graceful_shutdown if @status == :stop || @status == :restart
420
320
  if queue_requests
321
+ @queue_requests = false
421
322
  @reactor.clear!
422
323
  @reactor.shutdown
423
324
  end
325
+ graceful_shutdown if @status == :stop || @status == :restart
424
326
  rescue Exception => e
425
- STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
426
- STDERR.puts e.backtrace
327
+ @events.unknown_error e, nil, "Exception handling servers"
427
328
  ensure
428
- @check.close
329
+ begin
330
+ @check.close unless @check.closed?
331
+ rescue Errno::EBADF, RuntimeError
332
+ # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
333
+ # Errno::EBADF is infrequently raised
334
+ end
429
335
  @notify.close
336
+ @notify = nil
337
+ @check = nil
430
338
  end
431
339
 
432
340
  @events.fire :state, :done
@@ -463,6 +371,8 @@ module Puma
463
371
  clean_thread_locals = @options[:clean_thread_locals]
464
372
  close_socket = true
465
373
 
374
+ requests = 0
375
+
466
376
  while true
467
377
  case handle_request(client, buffer)
468
378
  when false
@@ -471,12 +381,24 @@ module Puma
471
381
  close_socket = false
472
382
  return
473
383
  when true
474
- return unless @queue_requests
475
384
  buffer.reset
476
385
 
477
386
  ThreadPool.clean_thread_locals if clean_thread_locals
478
387
 
479
- unless client.reset(@status == :run)
388
+ requests += 1
389
+
390
+ check_for_more_data = @status == :run
391
+
392
+ if requests >= MAX_FAST_INLINE
393
+ # This will mean that reset will only try to use the data it already
394
+ # has buffered and won't try to read more data. What this means is that
395
+ # every client, independent of their request speed, gets treated like a slow
396
+ # one once every MAX_FAST_INLINE requests.
397
+ check_for_more_data = false
398
+ end
399
+
400
+ unless client.reset(check_for_more_data)
401
+ return unless @queue_requests
480
402
  close_socket = false
481
403
  client.set_timeout @persistent_timeout
482
404
  @reactor.add client
@@ -499,24 +421,23 @@ module Puma
499
421
 
500
422
  close_socket = true
501
423
 
502
- @events.ssl_error self, addr, cert, e
424
+ @events.ssl_error e, addr, cert
503
425
 
504
426
  # The client doesn't know HTTP well
505
427
  rescue HttpParserError => e
506
428
  lowlevel_error(e, client.env)
507
429
 
508
- client.write_400
430
+ client.write_error(400)
509
431
 
510
- @events.parse_error self, client.env, e
432
+ @events.parse_error e, client
511
433
 
512
434
  # Server error
513
435
  rescue StandardError => e
514
436
  lowlevel_error(e, client.env)
515
437
 
516
- client.write_500
517
-
518
- @events.unknown_error self, e, "Read"
438
+ client.write_error(500)
519
439
 
440
+ @events.unknown_error e, nil, "Read"
520
441
  ensure
521
442
  buffer.reset
522
443
 
@@ -526,7 +447,7 @@ module Puma
526
447
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
527
448
  # Already closed
528
449
  rescue StandardError => e
529
- @events.unknown_error self, e, "Client"
450
+ @events.unknown_error e, nil, "Client"
530
451
  end
531
452
  end
532
453
  end
@@ -562,7 +483,7 @@ module Puma
562
483
 
563
484
  env[PATH_INFO] = env[REQUEST_PATH]
564
485
 
565
- # From http://www.ietf.org/rfc/rfc3875 :
486
+ # From https://www.ietf.org/rfc/rfc3875 :
566
487
  # "Script authors should be aware that the REMOTE_ADDR and
567
488
  # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
568
489
  # may not identify the ultimate source of the request.
@@ -609,6 +530,8 @@ module Puma
609
530
  #
610
531
  # Finally, it'll return +true+ on keep-alive connections.
611
532
  def handle_request(req, lines)
533
+ @requests_count +=1
534
+
612
535
  env = req.env
613
536
  client = req.io
614
537
 
@@ -640,6 +563,7 @@ module Puma
640
563
  headers.each_pair do |k, vs|
641
564
  if vs.respond_to?(:to_s) && !vs.to_s.empty?
642
565
  vs.to_s.split(NEWLINE).each do |v|
566
+ next if possible_header_injection?(v)
643
567
  fast_write client, "#{k}: #{v}\r\n"
644
568
  end
645
569
  else
@@ -648,12 +572,44 @@ module Puma
648
572
  end
649
573
 
650
574
  fast_write client, "\r\n".freeze
651
- rescue ConnectionError
575
+ rescue ConnectionError => e
576
+ @events.debug_error e
652
577
  # noop, if we lost the socket we just won't send the early hints
653
578
  end
654
579
  }
655
580
  end
656
581
 
582
+ # Fixup any headers with , in the name to have _ now. We emit
583
+ # headers with , in them during the parse phase to avoid ambiguity
584
+ # with the - to _ conversion for critical headers. But here for
585
+ # compatibility, we'll convert them back. This code is written to
586
+ # avoid allocation in the common case (ie there are no headers
587
+ # with , in their names), that's why it has the extra conditionals.
588
+
589
+ to_delete = nil
590
+ to_add = nil
591
+
592
+ env.each do |k,v|
593
+ if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
594
+ if to_delete
595
+ to_delete << k
596
+ else
597
+ to_delete = [k]
598
+ end
599
+
600
+ unless to_add
601
+ to_add = {}
602
+ end
603
+
604
+ to_add[k.tr(",", "_")] = v
605
+ end
606
+ end
607
+
608
+ if to_delete
609
+ to_delete.each { |k| env.delete(k) }
610
+ env.merge! to_add
611
+ end
612
+
657
613
  # A rack extension. If the app writes #call'ables to this
658
614
  # array, we will invoke them when the request is done.
659
615
  #
@@ -675,17 +631,14 @@ module Puma
675
631
  return :async
676
632
  end
677
633
  rescue ThreadPool::ForceShutdown => e
678
- @events.log "Detected force shutdown of a thread, returning 503"
679
- @events.unknown_error self, e, "Rack app"
680
-
681
- status = 503
682
- headers = {}
683
- res_body = ["Request was internally terminated early\n"]
634
+ @events.unknown_error e, req, "Rack app"
635
+ @events.log "Detected force shutdown of a thread"
684
636
 
637
+ status, headers, res_body = lowlevel_error(e, env, 503)
685
638
  rescue Exception => e
686
- @events.unknown_error self, e, "Rack app", env
639
+ @events.unknown_error e, req, "Rack app"
687
640
 
688
- status, headers, res_body = lowlevel_error(e, env)
641
+ status, headers, res_body = lowlevel_error(e, env, 500)
689
642
  end
690
643
 
691
644
  content_length = nil
@@ -700,10 +653,10 @@ module Puma
700
653
  line_ending = LINE_END
701
654
  colon = COLON
702
655
 
703
- http_11 = if env[HTTP_VERSION] == HTTP_11
656
+ http_11 = env[HTTP_VERSION] == HTTP_11
657
+ if http_11
704
658
  allow_chunked = true
705
659
  keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
706
- include_keepalive_header = false
707
660
 
708
661
  # An optimization. The most common response is 200, so we can
709
662
  # reply with the proper 200 status without having to compute
@@ -717,11 +670,9 @@ module Puma
717
670
 
718
671
  no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
719
672
  end
720
- true
721
673
  else
722
674
  allow_chunked = false
723
675
  keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
724
- include_keepalive_header = keep_alive
725
676
 
726
677
  # Same optimization as above for HTTP/1.1
727
678
  #
@@ -733,14 +684,18 @@ module Puma
733
684
 
734
685
  no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
735
686
  end
736
- false
737
687
  end
738
688
 
689
+ # regardless of what the client wants, we always close the connection
690
+ # if running without request queueing
691
+ keep_alive &&= @queue_requests
692
+
739
693
  response_hijack = nil
740
694
 
741
695
  headers.each do |k, vs|
742
696
  case k.downcase
743
697
  when CONTENT_LENGTH2
698
+ next if possible_header_injection?(vs)
744
699
  content_length = vs
745
700
  next
746
701
  when TRANSFER_ENCODING
@@ -753,6 +708,7 @@ module Puma
753
708
 
754
709
  if vs.respond_to?(:to_s) && !vs.to_s.empty?
755
710
  vs.to_s.split(NEWLINE).each do |v|
711
+ next if possible_header_injection?(v)
756
712
  lines.append k, colon, v, line_ending
757
713
  end
758
714
  else
@@ -760,10 +716,15 @@ module Puma
760
716
  end
761
717
  end
762
718
 
763
- if include_keepalive_header
764
- lines << CONNECTION_KEEP_ALIVE
765
- elsif http_11 && !keep_alive
766
- lines << CONNECTION_CLOSE
719
+ # HTTP/1.1 & 1.0 assume different defaults:
720
+ # - HTTP 1.0 assumes the connection will be closed if not specified
721
+ # - HTTP 1.1 assumes the connection will be kept alive if not specified.
722
+ # Only set the header if we're doing something which is not the default
723
+ # for this protocol version
724
+ if http_11
725
+ lines << CONNECTION_CLOSE if !keep_alive
726
+ else
727
+ lines << CONNECTION_KEEP_ALIVE if keep_alive
767
728
  end
768
729
 
769
730
  if no_body
@@ -890,19 +851,21 @@ module Puma
890
851
 
891
852
  # A fallback rack response if +@app+ raises as exception.
892
853
  #
893
- def lowlevel_error(e, env)
854
+ def lowlevel_error(e, env, status=500)
894
855
  if handler = @options[:lowlevel_error_handler]
895
856
  if handler.arity == 1
896
857
  return handler.call(e)
897
- else
858
+ elsif handler.arity == 2
898
859
  return handler.call(e, env)
860
+ else
861
+ return handler.call(e, env, status)
899
862
  end
900
863
  end
901
864
 
902
865
  if @leak_stack_on_error
903
- [500, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
866
+ [status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
904
867
  else
905
- [500, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
868
+ [status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
906
869
  end
907
870
  end
908
871
 
@@ -960,9 +923,10 @@ module Puma
960
923
  end
961
924
 
962
925
  def notify_safely(message)
926
+ @check, @notify = Puma::Util.pipe unless @notify
963
927
  begin
964
928
  @notify << message
965
- rescue IOError
929
+ rescue IOError, NoMethodError, Errno::EPIPE
966
930
  # The server, in another thread, is shutting down
967
931
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
968
932
  rescue RuntimeError => e
@@ -989,8 +953,9 @@ module Puma
989
953
  @thread.join if @thread && sync
990
954
  end
991
955
 
992
- def begin_restart
956
+ def begin_restart(sync=false)
993
957
  notify_safely(RESTART_COMMAND)
958
+ @thread.join if @thread && sync
994
959
  end
995
960
 
996
961
  def fast_write(io, str)
@@ -1023,5 +988,20 @@ module Puma
1023
988
  def shutting_down?
1024
989
  @status == :stop || @status == :restart
1025
990
  end
991
+
992
+ def possible_header_injection?(header_value)
993
+ HTTP_INJECTION_REGEX =~ header_value.to_s
994
+ end
995
+ private :possible_header_injection?
996
+
997
+ # List of methods invoked by #stats.
998
+ # @version 5.0.0
999
+ STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
1000
+
1001
+ # Returns a hash of stats about the running server for reporting purposes.
1002
+ # @version 5.0.0
1003
+ def stats
1004
+ STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
1005
+ end
1026
1006
  end
1027
1007
  end