puma 2.16.0-java → 3.0.0-java

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.

@@ -0,0 +1,23 @@
1
+ require 'puma/plugin'
2
+
3
+ Puma::Plugin.create do
4
+ def start(launcher)
5
+ path = File.join("tmp", "restart.txt")
6
+
7
+ File.write path, ""
8
+
9
+ orig = File.stat(path).mtime
10
+
11
+ in_background do
12
+ while true
13
+ sleep 2
14
+
15
+ if File.stat(path).mtime > orig
16
+ launcher.restart
17
+ break
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+
Binary file
data/lib/puma/runner.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  module Puma
2
2
  class Runner
3
- def initialize(cli)
4
- @cli = cli
3
+ def initialize(cli, events)
4
+ @launcher = cli
5
+ @events = events
5
6
  @options = cli.options
6
7
  @app = nil
7
8
  @control = nil
@@ -16,15 +17,19 @@ module Puma
16
17
  end
17
18
 
18
19
  def log(str)
19
- @cli.log str
20
+ @events.log str
21
+ end
22
+
23
+ def before_restart
24
+ @control.stop(true) if @control
20
25
  end
21
26
 
22
27
  def error(str)
23
- @cli.error str
28
+ @events.error str
24
29
  end
25
30
 
26
- def before_restart
27
- @control.stop(true) if @control
31
+ def debug(str)
32
+ @events.log "- #{str}" if @options[:debug]
28
33
  end
29
34
 
30
35
  def start_control
@@ -35,13 +40,13 @@ module Puma
35
40
 
36
41
  uri = URI.parse str
37
42
 
38
- app = Puma::App::Status.new @cli
43
+ app = Puma::App::Status.new @launcher
39
44
 
40
45
  if token = @options[:control_auth_token]
41
46
  app.auth_token = token unless token.empty? or token == :none
42
47
  end
43
48
 
44
- control = Puma::Server.new app, @cli.events
49
+ control = Puma::Server.new app, @launcher.events
45
50
  control.min_threads = 0
46
51
  control.max_threads = 1
47
52
 
@@ -104,34 +109,34 @@ module Puma
104
109
  end
105
110
 
106
111
  def load_and_bind
107
- unless @cli.config.app_configured?
112
+ unless @launcher.config.app_configured?
108
113
  error "No application configured, nothing to run"
109
114
  exit 1
110
115
  end
111
116
 
112
117
  # Load the app before we daemonize.
113
118
  begin
114
- @app = @cli.config.app
119
+ @app = @launcher.config.app
115
120
  rescue Exception => e
116
121
  log "! Unable to load application: #{e.class}: #{e.message}"
117
122
  raise e
118
123
  end
119
124
 
120
- @cli.binder.parse @options[:binds], self
125
+ @launcher.binder.parse @options[:binds], self
121
126
  end
122
127
 
123
128
  def app
124
- @app ||= @cli.config.app
129
+ @app ||= @launcher.config.app
125
130
  end
126
131
 
127
132
  def start_server
128
133
  min_t = @options[:min_threads]
129
134
  max_t = @options[:max_threads]
130
135
 
131
- server = Puma::Server.new app, @cli.events, @options
136
+ server = Puma::Server.new app, @launcher.events, @options
132
137
  server.min_threads = min_t
133
138
  server.max_threads = max_t
134
- server.inherit_binder @cli.binder
139
+ server.inherit_binder @launcher.binder
135
140
 
136
141
  if @options[:mode] == :tcp
137
142
  server.tcp_mode!
data/lib/puma/server.rb CHANGED
@@ -85,6 +85,7 @@ module Puma
85
85
  forward :add_tcp_listener, :@binder
86
86
  forward :add_ssl_listener, :@binder
87
87
  forward :add_unix_listener, :@binder
88
+ forward :connected_port, :@binder
88
89
 
89
90
  def inherit_binder(bind)
90
91
  @binder = bind
@@ -398,6 +399,7 @@ module Puma
398
399
  #
399
400
  def process_client(client, buffer)
400
401
  begin
402
+ clean_thread_locals = @options[:clean_thread_locals]
401
403
  close_socket = true
402
404
 
403
405
  while true
@@ -411,6 +413,8 @@ module Puma
411
413
  return unless @queue_requests
412
414
  buffer.reset
413
415
 
416
+ ThreadPool.clean_thread_locals if clean_thread_locals
417
+
414
418
  unless client.reset(@status == :run)
415
419
  close_socket = false
416
420
  client.set_timeout @persistent_timeout
@@ -516,63 +520,20 @@ module Puma
516
520
  env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
517
521
  end
518
522
 
519
- # Given the request +env+ from +client+ and a partial request body
520
- # in +body+, finish reading the body if there is one and invoke
521
- # the rack app. Then construct the response and write it back to
522
- # +client+
523
- #
524
- # +cl+ is the previously fetched Content-Length header if there
525
- # was one. This is an optimization to keep from having to look
526
- # it up again.
527
- #
528
- def handle_request(req, lines)
529
- env = req.env
523
+ def handle_app_response(req, lines, env, status, headers, res_body)
530
524
  client = req.io
531
525
 
532
- normalize_env env, req
533
-
534
- env[PUMA_SOCKET] = client
535
-
536
- if env[HTTPS_KEY] && client.peercert
537
- env[PUMA_PEERCERT] = client.peercert
538
- end
539
-
540
- env[HIJACK_P] = true
541
- env[HIJACK] = req
542
-
543
526
  body = req.body
544
527
 
545
528
  head = env[REQUEST_METHOD] == HEAD
546
529
 
547
- env[RACK_INPUT] = body
548
- env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
549
-
550
530
  # A rack extension. If the app writes #call'ables to this
551
531
  # array, we will invoke them when the request is done.
552
532
  #
553
- after_reply = env[RACK_AFTER_REPLY] = []
533
+ # (This is already initialized in `handle_request`, just getting here)
534
+ after_reply = env[RACK_AFTER_REPLY]
554
535
 
555
536
  begin
556
- begin
557
- status, headers, res_body = @app.call(env)
558
-
559
- return :async if req.hijacked
560
-
561
- status = status.to_i
562
-
563
- if status == -1
564
- unless headers.empty? and res_body == []
565
- raise "async response must have empty headers and body"
566
- end
567
-
568
- return :async
569
- end
570
- rescue StandardError => e
571
- @events.unknown_error self, e, "Rack app"
572
-
573
- status, headers, res_body = lowlevel_error(e)
574
- end
575
-
576
537
  content_length = nil
577
538
  no_body = head
578
539
 
@@ -714,6 +675,75 @@ module Puma
714
675
  return keep_alive
715
676
  end
716
677
 
678
+ # Given the request +env+ from +client+ and a partial request body
679
+ # in +body+, finish reading the body if there is one and invoke
680
+ # the rack app. Then construct the response and write it back to
681
+ # +client+
682
+ #
683
+ # +cl+ is the previously fetched Content-Length header if there
684
+ # was one. This is an optimization to keep from having to look
685
+ # it up again.
686
+ #
687
+ def handle_request(req, lines)
688
+ env = req.env
689
+ client = req.io
690
+
691
+ normalize_env env, req
692
+
693
+ env[PUMA_SOCKET] = client
694
+
695
+ if env[HTTPS_KEY] && client.peercert
696
+ env[PUMA_PEERCERT] = client.peercert
697
+ end
698
+
699
+ env[HIJACK_P] = true
700
+ env[HIJACK] = req
701
+
702
+ env['async.callback'] = lambda { |__response|
703
+ _status, _headers, _res_body = __response
704
+ # Faye websocket gem also calls this (when available)
705
+ # with status=101, and res_body being a stream
706
+ # but calling here just finishes the response
707
+ # that's why we only response if the request is not
708
+ # hijacked. not sure if this is a correct solution
709
+ # but working. for now.
710
+ unless req.hijacked
711
+ handle_app_response(req, lines, env, _status, _headers, _res_body)
712
+ end
713
+ }
714
+
715
+ body = req.body
716
+
717
+ env[RACK_INPUT] = body
718
+ env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
719
+
720
+ env[RACK_AFTER_REPLY] = []
721
+
722
+ begin
723
+ status, headers, res_body = @app.call(env)
724
+
725
+ return :async if req.hijacked
726
+
727
+ status = status.to_i
728
+
729
+ if status == -1
730
+ # unless headers.empty? and res_body == []
731
+ # puts "HEADERS: ".white.on_green + headers.inspect
732
+ # puts "BODY: ".white.on_green + res_body.inspect
733
+ # raise "async response must have empty headers and body"
734
+ # end
735
+
736
+ return :async
737
+ end
738
+ rescue StandardError => e
739
+ @events.unknown_error self, e, "Rack app"
740
+
741
+ status, headers, res_body = lowlevel_error(e, env)
742
+ end
743
+
744
+ return handle_app_response(req, lines, env, status, headers, res_body)
745
+ end
746
+
717
747
  def fetch_status_code(status)
718
748
  HTTP_STATUS_CODES.fetch(status) { 'CUSTOM' }
719
749
  end
@@ -775,9 +805,13 @@ module Puma
775
805
 
776
806
  # A fallback rack response if +@app+ raises as exception.
777
807
  #
778
- def lowlevel_error(e)
808
+ def lowlevel_error(e, env)
779
809
  if handler = @options[:lowlevel_error_handler]
780
- return handler.call(e)
810
+ if handler.arity == 1
811
+ return handler.call(e)
812
+ else
813
+ return handler.call(e, env)
814
+ end
781
815
  end
782
816
 
783
817
  if @leak_stack_on_error
data/lib/puma/single.rb CHANGED
@@ -27,7 +27,12 @@ module Puma
27
27
  end
28
28
 
29
29
  def jruby_daemon?
30
- daemon? and @cli.jruby?
30
+ daemon? and Puma.jruby?
31
+ end
32
+
33
+ def jruby_daemon_start
34
+ require 'puma/jruby_restart'
35
+ JRubyRestart.daemon_start(@restart_dir, restart_args)
31
36
  end
32
37
 
33
38
  def run
@@ -66,7 +71,7 @@ module Puma
66
71
  exit 1
67
72
  end
68
73
 
69
- pid = @cli.jruby_daemon_start
74
+ pid = jruby_daemon_start
70
75
  sleep
71
76
  end
72
77
  else
@@ -79,7 +84,7 @@ module Puma
79
84
  load_and_bind
80
85
  end
81
86
 
82
- @cli.write_state
87
+ @launcher.write_state
83
88
 
84
89
  start_control
85
90
 
@@ -90,7 +95,7 @@ module Puma
90
95
  redirect_io
91
96
  end
92
97
 
93
- @cli.events.fire_on_booted!
98
+ @launcher.events.fire_on_booted!
94
99
 
95
100
  begin
96
101
  server.run.join
@@ -0,0 +1,29 @@
1
+ require 'yaml'
2
+
3
+ module Puma
4
+ class StateFile
5
+ def initialize
6
+ @options = {}
7
+ end
8
+
9
+ def save(path)
10
+ File.write path, YAML.dump(@options)
11
+ end
12
+
13
+ def load(path)
14
+ @options = YAML.load File.read(path)
15
+ end
16
+
17
+ FIELDS = %w!control_url control_auth_token pid!
18
+
19
+ FIELDS.each do |f|
20
+ define_method f do
21
+ @options[f]
22
+ end
23
+
24
+ define_method "#{f}=" do |v|
25
+ @options[f] = v
26
+ end
27
+ end
28
+ end
29
+ end
@@ -45,6 +45,12 @@ module Puma
45
45
  attr_reader :spawned, :trim_requested
46
46
  attr_accessor :clean_thread_locals
47
47
 
48
+ def self.clean_thread_locals
49
+ Thread.current.keys.each do |key|
50
+ Thread.current[key] = nil unless key == :__recursive_key__
51
+ end
52
+ end
53
+
48
54
  # How many objects have yet to be processed by the pool?
49
55
  #
50
56
  def backlog
@@ -77,6 +83,7 @@ module Puma
77
83
  if @trim_requested > 0
78
84
  @trim_requested -= 1
79
85
  continue = false
86
+ not_full.signal
80
87
  break
81
88
  end
82
89
 
@@ -97,9 +104,7 @@ module Puma
97
104
  break unless continue
98
105
 
99
106
  if @clean_thread_locals
100
- Thread.current.keys.each do |key|
101
- Thread.current[key] = nil unless key == :__recursive_key__
102
- end
107
+ ThreadPool.clean_thread_locals
103
108
  end
104
109
 
105
110
  begin
@@ -5,48 +5,55 @@ module Rack
5
5
  module Handler
6
6
  module Puma
7
7
  DEFAULT_OPTIONS = {
8
- :Host => '0.0.0.0',
9
- :Port => 8080,
10
- :Threads => '0:16',
11
8
  :Verbose => false,
12
- :Silent => false
9
+ :Silent => false
13
10
  }
14
11
 
15
12
  def self.run(app, options = {})
16
13
  options = DEFAULT_OPTIONS.merge(options)
17
14
 
18
- if options[:Verbose]
19
- app = Rack::CommonLogger.new(app, STDOUT)
20
- end
15
+ conf = ::Puma::Configuration.new do |c|
16
+ c.quiet
21
17
 
22
- if options[:environment]
23
- ENV['RACK_ENV'] = options[:environment].to_s
24
- end
18
+ if options.delete(:Verbose)
19
+ app = Rack::CommonLogger.new(app, STDOUT)
20
+ end
21
+
22
+ if options[:environment]
23
+ c.environment options[:environment]
24
+ end
25
+
26
+ if options[:Threads]
27
+ min, max = options.delete(:Threads).split(':', 2)
28
+ c.threads min, max
29
+ end
25
30
 
26
- events_hander = options[:Silent] ? ::Puma::Events.strings : ::Puma::Events.stdio
27
- server = ::Puma::Server.new(app, events_hander)
28
- min, max = options[:Threads].split(':', 2)
31
+ host = options[:Host]
29
32
 
30
- log = events_hander.stdout
33
+ if host && (host[0,1] == '.' || host[0,1] == '/')
34
+ c.bind "unix://#{host}"
35
+ else
36
+ host ||= ::Puma::Configuration::DefaultTCPHost
37
+ port = options[:Port] || ::Puma::Configuration::DefaultTCPPort
31
38
 
32
- log.puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
33
- log.puts "* Min threads: #{min}, max threads: #{max}"
34
- log.puts "* Environment: #{ENV['RACK_ENV']}"
35
- log.puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
39
+ c.port port, host
40
+ end
36
41
 
37
- server.add_tcp_listener options[:Host], options[:Port]
38
- server.min_threads = min
39
- server.max_threads = max
40
- yield server if block_given?
42
+ c.app app
43
+ end
44
+
45
+ events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
41
46
 
47
+ launcher = ::Puma::Launcher.new(conf, :events => events)
48
+
49
+ yield launcher if block_given?
42
50
  begin
43
- server.run.join
51
+ launcher.run
44
52
  rescue Interrupt
45
- log.puts "* Gracefully stopping, waiting for requests to finish"
46
- server.stop(true)
47
- log.puts "* Goodbye!"
53
+ puts "* Gracefully stopping, waiting for requests to finish"
54
+ launcher.stop
55
+ puts "* Goodbye!"
48
56
  end
49
-
50
57
  end
51
58
 
52
59
  def self.valid_options